import Foundation
import Network
import NucleicProtocol

/// Browses Bonjour for nucleic hosts on the LAN (`_nucleic._tcp`) and resolves an endpoint to
/// connect to. The phone tries the QR's explicit LAN hint first (fast path), then falls back
/// to a Bonjour match on the host's identity fingerprint.
@MainActor
final class LANDiscovery: ObservableObject {
    struct Found: Identifiable, Hashable {
        let id: String          // endpoint description
        let endpoint: NWEndpoint
        let fingerprint: String?
        let name: String
    }

    @Published private(set) var hosts: [Found] = []
    private var browser: NWBrowser?

    func start() {
        guard browser == nil else { return }
        let params = NWParameters()
        params.includePeerToPeer = true
        let browser = NWBrowser(
            for: .bonjourWithTXTRecord(type: LANService.type, domain: nil), using: params)
        self.browser = browser
        browser.browseResultsChangedHandler = { [weak self] results, _ in
            Task { @MainActor in self?.apply(results) }
        }
        browser.start(queue: .main)
    }

    func stop() {
        browser?.cancel()
        browser = nil
        hosts = []
    }

    /// Resolve an endpoint to dial: explicit host:port from the QR if present, else a Bonjour
    /// result whose advertised fingerprint matches the pinned host key.
    func endpoint(forFingerprint fingerprint: String?, lanHost: String?, lanPort: UInt16?) -> NWEndpoint? {
        if let lanHost, let lanPort, let port = NWEndpoint.Port(rawValue: lanPort) {
            return .hostPort(host: .init(lanHost), port: port)
        }
        if let fingerprint, let match = hosts.first(where: { $0.fingerprint == fingerprint }) {
            return match.endpoint
        }
        return hosts.first?.endpoint
    }

    private func apply(_ results: Set<NWBrowser.Result>) {
        hosts = results.map { result in
            var fingerprint: String?
            var name = "Mac"
            if case .bonjour(let txt) = result.metadata {
                fingerprint = txt["fp"]
            }
            if case .service(let serviceName, _, _, _) = result.endpoint {
                name = serviceName
            }
            return Found(
                id: "\(result.endpoint)", endpoint: result.endpoint,
                fingerprint: fingerprint, name: name)
        }
    }
}

enum LANService {
    static let type = "_nucleic._tcp"
}
