// ResumeCursorTests.swift
// Unit tests for ResumeCursor — persistence of IC-1 sequence numbers.
//
// Uses a named UserDefaults suite per test to stay completely isolated from
// the real app defaults and from other test cases.

import XCTest
@testable import piRemote

final class ResumeCursorTests: XCTestCase {

    // Each test creates its own defaults suite; tearDown removes it.
    private var suiteName: String!
    private var defaults:  UserDefaults!
    private var cursor:    ResumeCursor!

    override func setUp() {
        super.setUp()
        suiteName = "ResumeCursorTests.\(UUID().uuidString)"
        defaults  = UserDefaults(suiteName: suiteName)!
        cursor    = ResumeCursor(defaults: defaults)
    }

    override func tearDown() {
        defaults.removePersistentDomain(forName: suiteName)
        super.tearDown()
    }

    // =========================================================================
    // MARK: 1. Initial state
    // =========================================================================

    func testLastSeq_freshSession_returnsNil() {
        XCTAssertNil(cursor.lastSeq(for: "session-A"),
                     "No stored cursor → must return nil")
    }

    // =========================================================================
    // MARK: 2. Save + load
    // =========================================================================

    func testUpdateAndLoad_returnsStoredValue() {
        cursor.update(sessionId: "s1", seq: 42)
        XCTAssertEqual(cursor.lastSeq(for: "s1"), 42)
    }

    func testUpdate_overwrites_previousValue() {
        cursor.update(sessionId: "s1", seq: 1)
        cursor.update(sessionId: "s1", seq: 999)
        XCTAssertEqual(cursor.lastSeq(for: "s1"), 999,
                       "Second update must overwrite the first")
    }

    func testUpdate_zeroSeq_isStoredAndDistinctFromMissing() {
        // Before update: nil
        XCTAssertNil(cursor.lastSeq(for: "s-zero"))
        // After update with 0: 0 (not nil)
        cursor.update(sessionId: "s-zero", seq: 0)
        XCTAssertEqual(cursor.lastSeq(for: "s-zero"), 0)
    }

    // =========================================================================
    // MARK: 3. UInt64 boundary values
    // =========================================================================

    func testUpdate_maxUInt64_roundTrips() {
        cursor.update(sessionId: "s-max", seq: UInt64.max)
        XCTAssertEqual(cursor.lastSeq(for: "s-max"), UInt64.max,
                       "UInt64.max must survive the Int64-bit-pattern round-trip")
    }

    func testUpdate_largeValue_roundTrips() {
        let large: UInt64 = 1_000_000_000_000
        cursor.update(sessionId: "s-large", seq: large)
        XCTAssertEqual(cursor.lastSeq(for: "s-large"), large)
    }

    // =========================================================================
    // MARK: 4. Clear
    // =========================================================================

    func testClear_removesEntry() {
        cursor.update(sessionId: "s1", seq: 100)
        cursor.clear(sessionId: "s1")
        XCTAssertNil(cursor.lastSeq(for: "s1"),
                     "After clear, lastSeq must return nil")
    }

    func testClear_missingEntry_isNoOp() {
        // Clearing a key that was never written must not throw or crash.
        cursor.clear(sessionId: "never-written")
        XCTAssertNil(cursor.lastSeq(for: "never-written"))
    }

    // =========================================================================
    // MARK: 5. Session isolation
    // =========================================================================

    func testIndependentSessions_doNotInterfere() {
        cursor.update(sessionId: "alpha", seq: 10)
        cursor.update(sessionId: "beta",  seq: 20)

        XCTAssertEqual(cursor.lastSeq(for: "alpha"), 10)
        XCTAssertEqual(cursor.lastSeq(for: "beta"),  20)
    }

    func testClearOneSession_doesNotAffectOther() {
        cursor.update(sessionId: "alpha", seq: 10)
        cursor.update(sessionId: "beta",  seq: 20)

        cursor.clear(sessionId: "alpha")

        XCTAssertNil(cursor.lastSeq(for: "alpha"),
                     "alpha should be nil after clear")
        XCTAssertEqual(cursor.lastSeq(for: "beta"), 20,
                       "beta must be unaffected by clearing alpha")
    }

    func testThreeIndependentSessions() {
        let ids: [String] = ["s1", "s2", "s3"]
        let seqs: [UInt64] = [1, 2, 3]
        zip(ids, seqs).forEach { cursor.update(sessionId: $0, seq: $1) }
        zip(ids, seqs).forEach {
            XCTAssertEqual(cursor.lastSeq(for: $0), $1)
        }
    }

    // =========================================================================
    // MARK: 6. Missing entry (never stored)
    // =========================================================================

    func testMissingEntry_returnsNil() {
        // Confirm nil for a key that was never written.
        XCTAssertNil(cursor.lastSeq(for: "completely-absent"))
    }
}
