import Foundation
import NucleicProtocol

/// The Mac-side sync server (SYNC_PROTOCOL §1: "host is the single authority"). Owns the
/// listener, the paired-device pins, and the active pairing secret; spawns a
/// `ConnectionHandler` per client and fans the bridge's broadcasts out to all of them.
/// Transport-agnostic — hand it a `LANListener` in the app or an in-memory one in tests.
public actor SyncHost {
    private let identity: DeviceIdentity
    private let bridge: any SyncHostBridge
    private let store: any PairedDeviceStore

    private var listener: (any SyncListener)?
    private var handlers: Set<ConnectionHandler> = []
    private var pairingSecret: Data?
    private var acceptTask: Task<Void, Never>?
    private var broadcastTask: Task<Void, Never>?

    public init(
        identity: DeviceIdentity,
        bridge: any SyncHostBridge,
        store: any PairedDeviceStore = InMemoryPairedDeviceStore()
    ) {
        self.identity = identity
        self.bridge = bridge
        self.store = store
    }

    // MARK: - Lifecycle

    public func start(listener: any SyncListener) throws {
        guard self.listener == nil else { return }
        self.listener = listener
        let connections = try listener.start()

        acceptTask = Task { [weak self] in
            for await connection in connections {
                await self?.accept(connection)
            }
        }
        broadcastTask = Task { [weak self, bridge] in
            for await broadcast in await bridge.broadcasts() {
                await self?.fanout(broadcast)
            }
        }
    }

    public func stop() async {
        acceptTask?.cancel(); acceptTask = nil
        broadcastTask?.cancel(); broadcastTask = nil
        listener?.stop(); listener = nil
        for handler in handlers { await handler.close() }
        handlers.removeAll()
        pairingSecret = nil
    }

    private func accept(_ connection: any FrameChannel) {
        let handler = ConnectionHandler(
            connection: connection, bridge: bridge,
            identity: identity, store: store, host: self)
        Task { await handler.run() }
    }

    // MARK: - Handler registry (called by ConnectionHandler)

    func register(_ handler: ConnectionHandler) { handlers.insert(handler) }
    func unregister(_ handler: ConnectionHandler) { handlers.remove(handler) }

    private func fanout(_ broadcast: HostBroadcast) async {
        for handler in handlers { await handler.deliver(broadcast) }
    }

    // MARK: - Pairing (SYNC §4.2)

    /// Begin a pairing window: mint a one-time secret and return the QR payload to display.
    /// The secret stays active until a device completes pairing (`didPair`) or `cancelPairing`.
    public func beginPairing(lanHost: String? = nil, lanPort: UInt16? = nil) async -> PairingPayload {
        let secret = PairingPayload.freshSecret()
        pairingSecret = secret
        return PairingPayload(
            hostName: await bridge.hostInfo.hostName,
            hostStaticKey: identity.staticPublicKey,
            pairingSecret: secret, lanHost: lanHost, lanPort: lanPort)
    }

    public func cancelPairing() { pairingSecret = nil }
    public func pairedDevices() async -> [PairedDevice] { await store.all() }
    public func revoke(deviceID: String) async { await store.remove(deviceID) }

    func activePairingSecret() -> Data? { pairingSecret }
    func didPair() { pairingSecret = nil }
}
