import Foundation
import NucleicProtocol

#if canImport(Network)
import Network

/// Browses Bonjour for sibling Macs on the LAN (`_nucleic._tcp`) and hands `PeerClient` the live
/// endpoint for a peer identified by its static-key fingerprint (the `fp` TXT record `LANListener`
/// advertises). This is the LAN half of mesh reconnect: a Mac's OS-assigned listener port changes
/// on every relaunch/update, so a stored `lanHint` goes stale — but the peer keeps advertising over
/// Bonjour, so matching on the (stable) fingerprint rediscovers it at its *current* address with no
/// stored hint at all. Same-subnet only; the relay covers the off-LAN case.
public final class LANBrowser: PeerLANBrowsing, @unchecked Sendable {
    private let lock = NSLock()
    private var services: [String: (name: String, domain: String)] = [:]
    private var browser: NWBrowser?
    private let queue = DispatchQueue(label: "nucleic.sync.browser")

    public init() {}

    /// Begin browsing (idempotent — a second call while already running is a no-op).
    public func start() {
        lock.lock()
        guard browser == nil else { lock.unlock(); return }
        let params = NWParameters()
        params.includePeerToPeer = true
        let browser = NWBrowser(
            for: .bonjourWithTXTRecord(type: LANListener.serviceType, domain: nil), using: params)
        browser.browseResultsChangedHandler = { [weak self] results, _ in
            self?.apply(results)
        }
        self.browser = browser
        lock.unlock()
        browser.start(queue: queue)
    }

    public func stop() {
        lock.lock()
        let browser = self.browser
        self.browser = nil
        services = [:]
        lock.unlock()
        browser?.cancel()
    }

    /// The current Bonjour endpoint for the peer whose static key hashes to `fingerprint`, or nil
    /// when no advertised service carries that fingerprint on this LAN right now.
    public func endpoint(forFingerprint fingerprint: String) async -> PeerEndpoint? {
        let match = lock.withLock { services[fingerprint] }
        guard let match else { return nil }
        return .bonjour(serviceName: match.name, domain: match.domain)
    }

    private func apply(_ results: Set<NWBrowser.Result>) {
        var map: [String: (name: String, domain: String)] = [:]
        for result in results {
            guard case .bonjour(let txt) = result.metadata, let fingerprint = txt["fp"],
                  case .service(let name, _, let domain, _) = result.endpoint
            else { continue }
            map[fingerprint] = (name, domain)
        }
        lock.withLock { services = map }
    }
}
#endif
