import Foundation
import Testing
import NucleicProtocol

@testable import NucleicCore

@Suite struct SyncHostTests {
    private func summary(_ id: String, status: SessionStatus = .running) -> WireSessionSummary {
        WireSessionSummary(
            sessionID: SessionID(rawValue: id), projectID: "p1", projectName: "ProjA",
            backend: .claudeCode, status: status, title: "auth-refactor", branch: "nucleic/auth",
            lastSeq: 10, diffStat: DiffStat(filesChanged: 2, added: 30, removed: 4),
            updatedAt: Date(timeIntervalSince1970: 1_700_000_000))
    }

    private func snapshot(for id: String) -> SessionSnapshot {
        SessionSnapshot(
            summary: summary(id, status: .awaitingApproval),
            recentEvents: [AgentEvent(
                sessionID: SessionID(rawValue: id), seq: 9, at: Date(timeIntervalSince1970: 1),
                backend: .claudeCode, nativeType: nil,
                kind: .assistantText(TextChunk(messageID: "m", text: "working", isPartial: false)))],
            pendingApprovals: [], cursor: 10)
    }

    /// Spin up host + client over an in-memory pipe and pair them, returning everything the
    /// test needs to drive the conversation.
    private func makePaired(
        bridge: FakeSyncBridge, deviceID: String = "iphone-1", clientChannel: ReleaseChannel? = nil
    ) async throws -> (host: SyncHost, hostIdentity: DeviceIdentity, client: SyncClient, recorder: EventRecorder, phone: DeviceIdentity) {
        let hostIdentity = DeviceIdentity()
        let phoneIdentity = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)

        let (clientCh, serverCh) = MemoryChannel.pair()
        try await host.start(listener: OneShotListener(serverCh))
        let payload = await host.beginPairing()

        let client = SyncClient(
            channel: clientCh, identity: phoneIdentity, hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: deviceID,
            deviceLabel: "Andrew's iPhone", releaseChannel: clientChannel)
        let recorder = EventRecorder()
        await recorder.consume(client.start())
        return (host, hostIdentity, client, recorder, phoneIdentity)
    }

    @Test func relayMembershipPushedAfterHello() async throws {
        // With a minter installed (the relay method is on), every registered device gets a
        // relayMembership push bound to its own deviceID — how pre-relay pairings adopt the
        // relay and how the token refreshes (mesh P2).
        let bridge = FakeSyncBridge(sessions: [])
        let hostIdentity = DeviceIdentity()
        let host = SyncHost(
            identity: hostIdentity, bridge: bridge, store: InMemoryPairedDeviceStore(),
            relayMembership: { deviceID in
                WireRelayMembership(roomID: "room-1", token: "m.\(deviceID)", exp: 123)
            })
        let (clientCh, serverCh) = MemoryChannel.pair()
        try await host.start(listener: OneShotListener(serverCh))
        let payload = await host.beginPairing()
        let client = SyncClient(
            channel: clientCh, identity: DeviceIdentity(), hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "iphone-9",
            deviceLabel: "Phone")
        let recorder = EventRecorder()
        await recorder.consume(client.start())

        let event = await recorder.waitFor {
            if case .relayMembership = $0 { return true } else { return false }
        }
        guard case .relayMembership(let membership) = event else {
            Issue.record("no relayMembership push"); return
        }
        #expect(membership.roomID == "room-1")
        #expect(membership.token == "m.iphone-9")
        await host.stop()
    }

    @Test func pairingQRCarriesRelayBootstrap() async throws {
        let bridge = FakeSyncBridge(sessions: [])
        let host = SyncHost(identity: DeviceIdentity(), bridge: bridge)
        let payload = await host.beginPairing(
            relay: WireRelayMembership(roomID: "room-2", token: "boot.tok", url: "https://edge.dev"))
        #expect(payload.relayRoomID == "room-2")
        #expect(payload.relayMembershipToken == "boot.tok")
        #expect(payload.relayURL == "https://edge.dev")
        // And it survives the QR round-trip old decoders already tolerate.
        let decoded = try PairingPayload(qrString: payload.qrString())
        #expect(decoded.relayMembershipToken == "boot.tok")
        await host.stop()
    }

    @Test func pairsAndWelcomesWithControlScope() async throws {
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let (host, _, _, recorder, phone) = try await makePaired(bridge: bridge)

        let ready = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("no welcome"); return }
        #expect(welcome.grantedScope == .control)
        #expect(welcome.host.hostName == "Andrew's Mac")
        #expect(welcome.capabilities.canModifyToolInput)

        // The host pinned this device with the phone's real static key.
        let devices = await host.pairedDevices()
        #expect(devices.count == 1)
        #expect(devices.first?.staticPublicKey == phone.staticPublicKey)
        #expect(devices.first?.deviceID == "iphone-1")
        await host.stop()
    }

    @Test func rejectsMismatchedReleaseChannel() async throws {
        // A beta host must refuse a canary remote (channels differ and neither pairing exception
        // applies). The remote surfaces the host's `.channelMismatch` and never pairs.
        let bridge = FakeSyncBridge(releaseChannel: .beta)
        let (host, _, _, recorder, _) = try await makePaired(bridge: bridge, clientChannel: .canary)

        let event = await recorder.waitFor {
            if case .wireError = $0 { return true } else if case .ready = $0 { return true } else { return false }
        }
        guard case .wireError(let error) = event else { Issue.record("expected channel rejection, got welcome"); return }
        #expect(error.code == .channelMismatch)
        #expect(await host.pairedDevices().isEmpty)
        await host.stop()
    }

    @Test func canaryRemoteMayPairWithLocalHost() async throws {
        // The deliberate exception: a canary remote is allowed onto a local dev host.
        let bridge = FakeSyncBridge(releaseChannel: .local)
        let (host, _, _, recorder, _) = try await makePaired(bridge: bridge, clientChannel: .canary)

        let ready = await recorder.waitFor { if case .ready = $0 { return true } else if case .wireError = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("expected welcome"); return }
        #expect(welcome.channel == .local)
        #expect(await host.pairedDevices().count == 1)
        await host.stop()
    }

    @Test func listAndSubscribeProjectHostState() async throws {
        let bridge = FakeSyncBridge(sessions: [summary("s1")], snapshots: [SessionID(rawValue: "s1"): snapshot(for: "s1")])
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        await client.send(.listSessions)
        let list = await recorder.waitFor { if case .sessionList = $0 { return true } else { return false } }
        guard case .sessionList(let summaries) = list else { Issue.record("no list"); return }
        #expect(summaries == [summary("s1")])

        await client.send(.subscribe(Subscribe(sessionID: SessionID(rawValue: "s1"), sinceSeq: nil, verbosity: .full)))
        let snap = await recorder.waitFor { if case .snapshot = $0 { return true } else { return false } }
        guard case .snapshot(let s) = snap else { Issue.record("no snapshot"); return }
        #expect(s.summary.sessionID == SessionID(rawValue: "s1"))
        #expect(s.cursor == 10)
        await host.stop()
    }

    @Test func listPeersReturnsTheHostsMeshPeers() async throws {
        // Mesh P4: a client asks for the host's peer list; the host answers from the bridge.
        let peers = [
            PeerSummary(deviceID: "mac-b", label: "Work Mac", kind: .mac,
                        capabilities: .hostAndAgents, online: true),
            PeerSummary(deviceID: "iphone-2", label: "iPhone", kind: .iphone),
        ]
        let bridge = FakeSyncBridge(peers: peers)
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        // The host advertises the capability so a real client would know it may ask.
        #expect(await bridge.capabilities.canListPeers == false)  // FakeSyncBridge default; real host sets true

        await client.send(.listPeers)
        let event = await recorder.waitFor { if case .peerList = $0 { return true } else { return false } }
        guard case .peerList(let got) = event else { Issue.record("no peerList"); return }
        #expect(got == peers)
        await host.stop()
    }

    @Test func peerRecordedWithDeviceKindAtPairing() async throws {
        // A Mac peer pairs advertising deviceKind=mac + host capabilities; the host pins it.
        let bridge = FakeSyncBridge()
        let hostIdentity = DeviceIdentity()
        let peerIdentity = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)
        let (clientCh, serverCh) = MemoryChannel.pair()
        try await host.start(listener: OneShotListener(serverCh))
        let payload = await host.beginPairing()

        let client = SyncClient(
            channel: clientCh, identity: peerIdentity, hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "mac-b",
            deviceLabel: "Work Mac", scopeClaim: .control,
            deviceKind: PeerKind.mac.rawValue,
            clientCaps: WireClientCapabilities(mesh: 1, canHost: true, canRunAgents: true))
        let recorder = EventRecorder()
        await recorder.consume(client.start())
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        let devices = await host.pairedDevices()
        #expect(devices.first?.kind == .mac)
        #expect(devices.first?.capabilities.canHost == true)
        #expect(devices.first?.capabilities.canRunAgents == true)
        await host.stop()
    }

    // MARK: Mesh P4 remainder — dedup, addresses, addressUpdate, mac-pairing confirm

    /// A second authenticated connection from the same device evicts the first after the
    /// grace period (keep-newest), while the new one stays up.
    @Test func duplicateConnectionForSameDeviceClosesTheOlder() async throws {
        let bridge = FakeSyncBridge()
        let hostIdentity = DeviceIdentity()
        let phoneIdentity = DeviceIdentity()
        let host = SyncHost(
            identity: hostIdentity, bridge: bridge, store: InMemoryPairedDeviceStore(),
            dedupGrace: .milliseconds(50))
        let listener = ManualListener()
        try await host.start(listener: listener)

        let (c1, s1) = MemoryChannel.pair()
        listener.push(s1)
        let payload = await host.beginPairing()
        let first = SyncClient(
            channel: c1, identity: phoneIdentity, hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "iphone-1", deviceLabel: "iPhone")
        let rec1 = EventRecorder()
        await rec1.consume(first.start())
        _ = await rec1.waitFor { if case .ready = $0 { return true } else { return false } }

        // Same device reconnects over a new socket (Wi-Fi blip, path change).
        let (c2, s2) = MemoryChannel.pair()
        listener.push(s2)
        let second = SyncClient(
            channel: c2, identity: phoneIdentity, hostStaticKey: hostIdentity.staticPublicKey,
            mode: .reconnect, deviceID: "iphone-1", deviceLabel: "iPhone")
        let rec2 = EventRecorder()
        await rec2.consume(second.start())
        _ = await rec2.waitFor { if case .ready = $0 { return true } else { return false } }

        // The older connection is closed once the grace elapses…
        _ = await rec1.waitFor { if case .closed = $0 { return true } else { return false } }
        // …and the newer one still answers.
        await second.send(.ping)
        await second.send(.listSessions)
        _ = await rec2.waitFor { if case .sessionList = $0 { return true } else { return false } }
        #expect(await host.connectedDeviceIDs() == ["iphone-1"])
        await host.stop()
    }

    /// Different devices coexist — dedup keys strictly on deviceID.
    @Test func distinctDevicesKeepBothConnections() async throws {
        let bridge = FakeSyncBridge()
        let hostIdentity = DeviceIdentity()
        let host = SyncHost(
            identity: hostIdentity, bridge: bridge, store: InMemoryPairedDeviceStore(),
            dedupGrace: .milliseconds(50))
        let listener = ManualListener()
        try await host.start(listener: listener)

        var recorders: [EventRecorder] = []
        for name in ["iphone-1", "mac-b"] {
            let (clientCh, serverCh) = MemoryChannel.pair()
            listener.push(serverCh)
            let payload = await host.beginPairing()
            let client = SyncClient(
                channel: clientCh, identity: DeviceIdentity(), hostStaticKey: payload.hostStaticKey,
                mode: .pair(secret: payload.pairingSecret), deviceID: name, deviceLabel: name)
            let recorder = EventRecorder()
            await recorder.consume(client.start())
            _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
            recorders.append(recorder)
        }

        // Outlive the dedup grace, then check both still hold connections.
        try await Task.sleep(for: .milliseconds(150))
        #expect(await host.connectedDeviceIDs() == ["iphone-1", "mac-b"])
        await host.stop()
    }

    /// `Hello.addresses` lands in the store at pairing; `Welcome.addresses` comes from the
    /// bridge — both ends refresh reconnect hints on every connect.
    @Test func addressesExchangedAtHello() async throws {
        let hostAddresses = PeerAddresses(lanHint: "192.168.1.5:52000", tailnet: "100.64.0.1")
        let clientAddresses = PeerAddresses(lanHint: "192.168.1.9:53111")
        let bridge = FakeSyncBridge(localAddresses: hostAddresses)
        let hostIdentity = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)
        let (clientCh, serverCh) = MemoryChannel.pair()
        try await host.start(listener: OneShotListener(serverCh))
        let payload = await host.beginPairing()

        let client = SyncClient(
            channel: clientCh, identity: DeviceIdentity(), hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "mac-b", deviceLabel: "Work Mac",
            deviceKind: PeerKind.mac.rawValue,
            clientCaps: WireClientCapabilities(mesh: 1, canHost: true, canRunAgents: true),
            addresses: clientAddresses)
        let recorder = EventRecorder()
        await recorder.consume(client.start())
        let ready = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("no welcome"); return }
        #expect(welcome.addresses == hostAddresses)
        let pinned = await poll { await store.all() }
        #expect(pinned.first?.addresses == clientAddresses)
        await host.stop()
    }

    /// `addressUpdate` refreshes the sender's own stored addresses mid-connection.
    @Test func addressUpdateRefreshesTheStore() async throws {
        let bridge = FakeSyncBridge()
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        let fresh = PeerAddresses(lanHint: "10.0.1.4:50123", tailnet: "100.64.0.2")
        await client.send(.addressUpdate(fresh))
        let updated = await poll { (await host.pairedDevices()).filter { $0.addresses == fresh } }
        #expect(updated.first?.deviceID == "iphone-1")
        await host.stop()
    }

    /// Pairing a Mac consults the bridge (the accepting Mac's user confirms); a decline
    /// rejects the pairing and pins nothing. Phones never hit the gate.
    @Test func macPairingDeclinedByBridgePinsNothing() async throws {
        let bridge = FakeSyncBridge()
        await bridge.setApproveMacPairing(false)
        let hostIdentity = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)
        let (clientCh, serverCh) = MemoryChannel.pair()
        try await host.start(listener: OneShotListener(serverCh))
        let payload = await host.beginPairing()

        let client = SyncClient(
            channel: clientCh, identity: DeviceIdentity(), hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "mac-b", deviceLabel: "Work Mac",
            scopeClaim: .control, deviceKind: PeerKind.mac.rawValue,
            clientCaps: WireClientCapabilities(mesh: 1, canHost: true, canRunAgents: true))
        let recorder = EventRecorder()
        await recorder.consume(client.start())
        let outcome = await recorder.waitFor {
            if case .wireError = $0 { return true }
            if case .ready = $0 { return true }
            return false
        }
        guard case .wireError(let wire) = outcome else { Issue.record("expected a decline"); return }
        #expect(wire.code == .unauthorized)
        #expect(await store.all().isEmpty)
        let asked = await bridge.macPairingRequests
        #expect(asked.first?.deviceID == "mac-b")
        await host.stop()
    }

    @Test func phonePairingSkipsTheMacConfirm() async throws {
        let bridge = FakeSyncBridge()
        await bridge.setApproveMacPairing(false)  // would fail a Mac; phones must not care
        let (host, _, _, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        #expect(await bridge.macPairingRequests.isEmpty)
        #expect(await host.pairedDevices().count == 1)
        await host.stop()
    }

    /// A pairing party may put any deviceID in the plaintext preamble. Claiming an *existing*
    /// device's id with a different key must be rejected, not silently rewrite that record.
    @Test func pairingWithAnExistingDeviceIDButDifferentKeyIsRejected() async throws {
        let bridge = FakeSyncBridge()
        let hostIdentity = DeviceIdentity()
        let victim = DeviceIdentity()
        let attacker = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)
        let listener = ManualListener()
        try await host.start(listener: listener)

        // Victim pairs normally as "iphone-1".
        let (c1, s1) = MemoryChannel.pair()
        listener.push(s1)
        let p1 = await host.beginPairing()
        let victimClient = SyncClient(
            channel: c1, identity: victim, hostStaticKey: p1.hostStaticKey,
            mode: .pair(secret: p1.pairingSecret), deviceID: "iphone-1", deviceLabel: "iPhone")
        let rec1 = EventRecorder()
        await rec1.consume(victimClient.start())
        _ = await rec1.waitFor { if case .ready = $0 { return true } else { return false } }

        // Attacker with the (fresh) QR secret claims the same deviceID under a different key.
        let (c2, s2) = MemoryChannel.pair()
        listener.push(s2)
        let p2 = await host.beginPairing()
        let attackerClient = SyncClient(
            channel: c2, identity: attacker, hostStaticKey: p2.hostStaticKey,
            mode: .pair(secret: p2.pairingSecret), deviceID: "iphone-1", deviceLabel: "Evil")
        let rec2 = EventRecorder()
        await rec2.consume(attackerClient.start())
        let outcome = await rec2.waitFor {
            if case .wireError = $0 { return true }
            if case .ready = $0 { return true }
            return false
        }
        guard case .wireError(let wire) = outcome else { Issue.record("expected key-mismatch reject"); return }
        #expect(wire.code == .unauthorized)
        // The victim's pin is untouched — still the victim's key, still labelled "iPhone".
        let record = await store.device("iphone-1")
        #expect(record?.staticPublicKey == victim.staticPublicKey)
        #expect(record?.label == "iPhone")
        await host.stop()
    }

    /// A device that paired as a phone can't silently promote itself to a session-owning mac on
    /// a later hello — that grant needs the same accepting-side confirm as a fresh mac pairing.
    @Test func reconnectPromotingToMacRequiresConfirm() async throws {
        let bridge = FakeSyncBridge()
        let hostIdentity = DeviceIdentity()
        let peer = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)
        let listener = ManualListener()
        try await host.start(listener: listener)

        // Pair as a plain phone (no confirm, granted control).
        let (c1, s1) = MemoryChannel.pair()
        listener.push(s1)
        let payload = await host.beginPairing()
        let phone = SyncClient(
            channel: c1, identity: peer, hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "dev", deviceLabel: "iPhone")
        let rec1 = EventRecorder()
        await rec1.consume(phone.start())
        _ = await rec1.waitFor { if case .ready = $0 { return true } else { return false } }
        #expect(await bridge.macPairingRequests.isEmpty)

        // Reconnect over IK claiming to be a mac; the accepting side declines the promotion.
        await bridge.setApproveMacPairing(false)
        let (c2, s2) = MemoryChannel.pair()
        listener.push(s2)
        let asMac = SyncClient(
            channel: c2, identity: peer, hostStaticKey: hostIdentity.staticPublicKey,
            mode: .reconnect, deviceID: "dev", deviceLabel: "Sneaky Mac",
            deviceKind: PeerKind.mac.rawValue,
            clientCaps: WireClientCapabilities(mesh: 1, canHost: true, canRunAgents: true))
        let rec2 = EventRecorder()
        await rec2.consume(asMac.start())
        let outcome = await rec2.waitFor {
            if case .wireError = $0 { return true }
            if case .ready = $0 { return true }
            return false
        }
        guard case .wireError = outcome else { Issue.record("expected promotion to be declined"); return }
        #expect(await bridge.macPairingRequests.first?.deviceID == "dev")
        // The record stays a phone with no host capabilities.
        let record = await store.device("dev")
        #expect(record?.kind == .iphone)
        #expect(record?.capabilities == PeerCapabilities.none)
        await host.stop()
    }

    @Test func approvalBroadcastAndFirstResponderWins() async throws {
        let sid = SessionID(rawValue: "s1")
        let bridge = FakeSyncBridge(sessions: [summary("s1")], snapshots: [sid: snapshot(for: "s1")])
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        // Must be subscribed to receive an approval push for that session.
        await client.send(.subscribe(Subscribe(sessionID: sid, sinceSeq: nil, verbosity: .full)))
        _ = await recorder.waitFor { if case .snapshot = $0 { return true } else { return false } }

        let approval = ApprovalRequest(
            id: ApprovalID(rawValue: "a1"), sessionID: sid, toolCallID: "t", toolName: "Bash",
            input: ["command": "rm -rf build/"], title: "Run command", risk: .destructive,
            createdAt: Date(timeIntervalSince1970: 2))
        await bridge.push(.approvalRequested(approval))

        let got = await recorder.waitFor { if case .approvalRequested = $0 { return true } else { return false } }
        guard case .approvalRequested(let req) = got else { Issue.record("no approval"); return }
        #expect(req.id == approval.id)

        await client.send(.approvalRespond(approval.id, .deny(reason: "nope")))
        // Wait (bounded) for the decision to traverse seal→decrypt→dispatch→bridge.
        let recorded = await poll { await bridge.respondedApprovals }
        #expect(recorded.count == 1)
        #expect(recorded.first?.0 == approval.id)
        // The remote answer is stamped with the authenticated peer's ResponderLabel — its
        // name paired with its identity fingerprint — not the local "mac-ui" placeholder.
        let responder = try #require(recorded.first?.2)
        let parts = ResponderLabel.split(responder)
        #expect(parts.fingerprint != nil)
        #expect(responder != "mac-ui")
        await host.stop()
    }

    @Test func sendInputReachesHost() async throws {
        let sid = SessionID(rawValue: "s1")
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        await client.send(.sendInput(sid, AgentInput(text: "keep going")))
        let inputs = await poll { await bridge.sentInputs }
        #expect(inputs.first?.0 == sid)
        #expect(inputs.first?.1.plainText == "keep going")
        await host.stop()
    }

    @Test func controlActionsReachHostAndGrantControlScope() async throws {
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        let ready = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("no welcome"); return }
        // Default pairing now grants full control parity.
        #expect(welcome.grantedScope == .control)

        let pid = ProjectID(rawValue: "p1")
        await client.send(.startChat(StartChatRequest(projectID: pid, message: "go")))
        await client.send(.captureTodo(CaptureTodoRequest(text: "idea", projectID: pid)))
        await client.send(.dispatchTodo(TodoID(rawValue: "t1"), pid))
        await client.send(.integrate(SessionID(rawValue: "s1"), .squash))
        await client.send(.setArchived(SessionID(rawValue: "s1"), true))

        let chats = await poll { await bridge.startedChats }
        #expect(chats.first?.message == "go")
        #expect(await poll { await bridge.capturedTodos }.first?.text == "idea")
        #expect(await poll { await bridge.dispatchedTodos }.first?.1 == pid)
        #expect(await poll { await bridge.integrations }.first?.1 == .squash)
        #expect(await poll { await bridge.archives }.first?.1 == true)
        await host.stop()
    }

    @Test func welcomeCarriesModelCatalog() async throws {
        let catalog = WireModelCatalog(
            groups: [[WireModelCatalog.Model(
                sku: "claude-opus-4-8", displayName: "Opus 4.8", backend: .claudeCode,
                contextBadge: "256K", contextWindow: 256_000, efforts: ["low", "high"],
                effortNoun: "Effort")]],
            effortDisplayNames: ["orchestra": "Orchestra"], orchestraSentinel: "orchestra",
            orchestraRequiresControlNote: "Requires Nucleic Control",
            fallbackModel: "claude-opus-4-8", fallbackEffort: "high")
        let bridge = FakeSyncBridge(modelCatalog: catalog, sessions: [summary("s1")])
        let (host, _, _, recorder, _) = try await makePaired(bridge: bridge)

        let ready = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("no welcome"); return }
        #expect(welcome.modelCatalog == catalog)
        #expect(welcome.modelCatalog.efforts(forModel: "claude-opus-4-8") == ["low", "high"])
        await host.stop()
    }

    @Test func midSessionSettersReachHost() async throws {
        let sid = SessionID(rawValue: "s1")
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        await client.send(.setSessionModel(sid, "claude-sonnet-4-6"))
        await client.send(.setSessionEffort(sid, "xhigh"))
        await client.send(.setSessionAuto(sid, true))
        await client.send(.setSessionAutoShip(sid, true))
        await client.send(.setSessionShipBranch(sid, "main"))
        let queuedID = UUID(uuidString: "44444444-4444-4444-4444-444444444444")!
        await client.send(.cancelQueuedMessage(sid, queuedID))

        #expect(await poll { await bridge.sessionModels }.first?.1 == "claude-sonnet-4-6")
        #expect(await poll { await bridge.sessionEfforts }.first?.1 == "xhigh")
        #expect(await poll { await bridge.sessionAutos }.first?.1 == true)
        #expect(await poll { await bridge.sessionAutoShips }.first?.1 == true)
        #expect(await poll { await bridge.sessionShipBranches }.first?.1 == "main")
        #expect(await poll { await bridge.cancelledQueued }.first?.1 == queuedID)
        await host.stop()
    }

    @Test func dashboardRequestReturnsSnapshot() async throws {
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let snapshot = DashboardSnapshot(
            counts: DashboardCounts(projects: 1, chats: 1, activeChats: 1, messages: 3, activeDays: 1),
            activity: [], projects: [], todos: [])
        await bridge.setDashboard(snapshot)
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        await client.send(.listDashboard)
        let got = await recorder.waitFor { if case .dashboard = $0 { return true } else { return false } }
        guard case .dashboard(let d) = got else { Issue.record("no dashboard"); return }
        #expect(d.counts.messages == 3)
        await host.stop()
    }

    @Test func lostApprovalRaceReturnsError() async throws {
        let sid = SessionID(rawValue: "s1")
        let bridge = FakeSyncBridge(sessions: [summary("s1")], snapshots: [sid: snapshot(for: "s1")])
        await bridge.setFailApproval(WireError(code: .alreadyResolved, message: "already answered on Mac", sessionID: sid))
        let (host, _, client, recorder, _) = try await makePaired(bridge: bridge)
        _ = await recorder.waitFor { if case .ready = $0 { return true } else { return false } }

        await client.send(.approvalRespond(ApprovalID(rawValue: "a1"), .allow()))
        let err = await recorder.waitFor { if case .wireError = $0 { return true } else { return false } }
        guard case .wireError(let wire) = err else { Issue.record("no error"); return }
        #expect(wire.code == .alreadyResolved)
        await host.stop()
    }

    @Test func reconnectWithPinnedKeyUsesIK() async throws {
        // First pair to populate the store.
        let bridge = FakeSyncBridge(sessions: [summary("s1")])
        let hostIdentity = DeviceIdentity()
        let phoneIdentity = DeviceIdentity()
        let store = InMemoryPairedDeviceStore()
        let host = SyncHost(identity: hostIdentity, bridge: bridge, store: store)

        let listener = ManualListener()
        try await host.start(listener: listener)

        let (c1, s1) = MemoryChannel.pair()
        listener.push(s1)
        let payload = await host.beginPairing()
        let pairClient = SyncClient(
            channel: c1, identity: phoneIdentity, hostStaticKey: payload.hostStaticKey,
            mode: .pair(secret: payload.pairingSecret), deviceID: "iphone-1", deviceLabel: "iPhone")
        let rec1 = EventRecorder()
        await rec1.consume(pairClient.start())
        _ = await rec1.waitFor { if case .ready = $0 { return true } else { return false } }
        await pairClient.disconnect()

        // Now reconnect over a fresh channel using IK against the pinned host static.
        let (c2, s2) = MemoryChannel.pair()
        listener.push(s2)
        let reconnectClient = SyncClient(
            channel: c2, identity: phoneIdentity, hostStaticKey: hostIdentity.staticPublicKey,
            mode: .reconnect, deviceID: "iphone-1", deviceLabel: "iPhone")
        let rec2 = EventRecorder()
        await rec2.consume(reconnectClient.start())
        let ready = await rec2.waitFor { if case .ready = $0 { return true } else { return false } }
        guard case .ready(let welcome) = ready else { Issue.record("no reconnect welcome"); return }
        #expect(welcome.grantedScope == .control)
        await host.stop()
    }
}
