import Foundation
import ImageCaptureCore

/// 记录最近一次 USB 相机会话断开，用于再次进入页面时触发 Browser 热重启重新枚举。
enum USBCameraReconnectHint {
    private(set) static var lastDisconnectedAt: Date?

    /// 标记 USB 相机已断开，供下次 start 判断是否需热重启。
    static func markDisconnected() {
        lastDisconnectedAt = Date()
    }

    /// 近期是否刚断开（60s 内），需要主动 re-enumerate。
    static var shouldReEnumerate: Bool {
        guard let lastDisconnectedAt else { return false }
        return Date().timeIntervalSince(lastDisconnectedAt) < 60
    }
}

/// 非 MainActor 的 USB 设备浏览器。
/// ICDeviceBrowserDelegate 不应挂在 @MainActor 类型上，否则部分系统版本上 didAdd 可能永不触发。
final class USBDeviceBrowserController: NSObject {
    var onCameraDiscovered: ((ICCameraDevice, String) -> Void)?
    var onCameraRemoved: ((ICDevice) -> Void)?
    var onAuthorizationDenied: (() -> Void)?
    var onScanTimeout: (() -> Void)?

    private let browser = ICDeviceBrowser()
    private var pollTimer: Timer?
    private var scanTimeoutTask: Task<Void, Never>?
    private var notifiedUUIDs: Set<String> = []
    private var pollTick = 0
    private var isAuthorized = false
    private var emptyPollStreak = 0
    private var isRestarting = false
    private var isStopped = false
    private var prefersCombinedBrowseMask = true
    private var isReEnumerating = false

    /// Browser 是否仍在运行（未 stop）。
    var isSessionBrowsing: Bool {
        browser.isBrowsing && !isStopped
    }

    override init() {
        super.init()
        browser.delegate = self
        configureBrowseMask()
    }

    /// 请求授权并启动设备搜索；若 Browser 已在运行则直接扫描已连接设备。
    func start() async -> Bool {
        isStopped = false
        browser.delegate = self

        if browser.isBrowsing {
            log("Browser 已在运行，扫描已连接 USB 相机…")
            prepareForNewSession()
            startScanTimeout()
            let found = await waitForDiscovery(maxAttempts: 8, intervalNanoseconds: 300_000_000)
            if !found, USBCameraReconnectHint.shouldReEnumerate {
                await reEnumerateConnectedDevices(reason: "再次进入")
            }
            return true
        }

        stopPolling()
        scanTimeoutTask?.cancel()
        pollTick = 0
        emptyPollStreak = 0
        notifiedUUIDs.removeAll()

        if !isAuthorized {
            let granted = await requestAuthorizations(on: browser)
            guard granted else {
                onAuthorizationDenied?()
                return false
            }
            isAuthorized = true
        }

        await beginBrowsing(label: "首次启动")
        startPolling()
        startScanTimeout()

        let foundImmediately = await waitForDiscovery(maxAttempts: 4, intervalNanoseconds: 300_000_000)
        if !foundImmediately {
            await reEnumerateConnectedDevices(reason: "首次空结果")
        }
        return true
    }

    /// 页面离开：保持 Browser 运行，仅重置发现状态供下次 attach。
    func pauseForSessionEnd() {
        guard !isStopped else { return }
        scanTimeoutTask?.cancel()
        scanTimeoutTask = nil
        notifiedUUIDs.removeAll()
        emptyPollStreak = 0
        pollTick = 0
        isReEnumerating = false
        isRestarting = false
        log("会话结束，Browser 保持运行等待再次 attach")
    }

    /// 立即扫描 browser.devices，用于 Browser 已运行时的再次进入。
    func discoverAlreadyConnectedDevices() {
        guard !isStopped, browser.isBrowsing else { return }
        pollDiscoveredDevices()
    }

    private func prepareForNewSession() {
        notifiedUUIDs.removeAll()
        emptyPollStreak = 0
        pollTick = 0
        if pollTimer == nil {
            startPolling()
        }
    }

    /// 在未发现设备时重启 Browser（热插拔 / 用户手动触发）。
    func restart() async {
        guard !isRestarting, !isReEnumerating else { return }
        isRestarting = true
        defer { isRestarting = false }

        guard isAuthorized else {
            _ = await start()
            return
        }

        log("手动/自动重启 Browser 搜索…")
        if browser.isBrowsing {
            browser.stop()
        }
        try? await Task.sleep(nanoseconds: 500_000_000)
        notifiedUUIDs.removeAll()
        pollTick = 0
        emptyPollStreak = 0
        scanTimeoutTask?.cancel()
        toggleBrowseMask()
        await beginBrowsing(label: "重启")
        startScanTimeout()
        _ = await waitForDiscovery(maxAttempts: 6, intervalNanoseconds: 300_000_000)
    }

    /// 停止搜索。
    func stop() {
        isStopped = true
        stopPolling()
        scanTimeoutTask?.cancel()
        scanTimeoutTask = nil
        pollTick = 0
        emptyPollStreak = 0
        if browser.isBrowsing {
            browser.stop()
        }
        browser.delegate = nil
        notifiedUUIDs.removeAll()
        isAuthorized = false
        isRestarting = false
    }

    /// 测试用：当前是否已缓存 USB 授权结果。
    var isAuthorizationGrantedForTesting: Bool { isAuthorized }
    /// 测试用：Browser 是否已停止。
    var isStoppedForTesting: Bool { isStopped }

    func resetNotifiedDevices() {
        notifiedUUIDs.removeAll()
    }

    private func configureBrowseMask() {
        applyBrowseMask(combined: prefersCombinedBrowseMask)
    }

    /// 切换 Camera|LocalUSB 与仅 Camera 掩码，提高 re-enumerate 成功率。
    private func toggleBrowseMask() {
        prefersCombinedBrowseMask.toggle()
        applyBrowseMask(combined: prefersCombinedBrowseMask)
    }

    private func applyBrowseMask(combined: Bool) {
        guard #available(iOS 15.2, *) else { return }
        let mask: ICDeviceTypeMask
        if combined {
            mask = ICDeviceTypeMask(
                rawValue: ICDeviceTypeMask.camera.rawValue | ICDeviceLocationTypeMask.local.rawValue
            ) ?? .camera
            log("browsedDeviceTypeMask=0x\(String(mask.rawValue, radix: 16)) (Camera|LocalUSB)")
        } else {
            mask = .camera
            log("browsedDeviceTypeMask=0x\(String(mask.rawValue, radix: 16)) (CameraOnly)")
        }
        browser.browsedDeviceTypeMask = mask
    }

    /// 轮询等待 didAdd / devices 列表出现相机。
    private func waitForDiscovery(maxAttempts: Int, intervalNanoseconds: UInt64) async -> Bool {
        for _ in 0 ..< maxAttempts {
            guard !isStopped else { return !notifiedUUIDs.isEmpty }
            try? await Task.sleep(nanoseconds: intervalNanoseconds)
            pollDiscoveredDevices()
            if !notifiedUUIDs.isEmpty { return true }
        }
        return !notifiedUUIDs.isEmpty
    }

    /// 会话关闭后 iOS 常不触发 didAdd；stop → wait → start 强制重新枚举仍连接的 USB 相机。
    private func reEnumerateConnectedDevices(reason: String) async {
        guard !isStopped, notifiedUUIDs.isEmpty, !isReEnumerating else { return }
        isReEnumerating = true
        defer { isReEnumerating = false }

        for attempt in 1 ... 2 {
            guard !isStopped, notifiedUUIDs.isEmpty else { return }
            log("\(reason)：第 \(attempt) 次热重启 Browser 以重新枚举 USB 相机…")

            if browser.isBrowsing {
                browser.stop()
            }
            try? await Task.sleep(nanoseconds: UInt64(attempt) * 500_000_000)
            toggleBrowseMask()
            notifiedUUIDs.removeAll()
            pollTick = 0
            emptyPollStreak = 0

            await beginBrowsing(label: "\(reason)-热重启\(attempt)")
            if await waitForDiscovery(maxAttempts: 6, intervalNanoseconds: 300_000_000) {
                log("\(reason)：热重启第 \(attempt) 次成功发现相机")
                return
            }
        }
        log("\(reason)：热重启后仍未发现 USB 相机")
    }

    private func beginBrowsing(label: String) async {
        log("\(label) ICDeviceBrowser.start() suspended=\(browser.isSuspended)")
        browser.start()
        log("isBrowsing=\(browser.isBrowsing) devices.count=\(browser.devices?.count ?? 0)")
    }

    private func stopPolling() {
        pollTimer?.invalidate()
        pollTimer = nil
    }

    private func startPolling() {
        DispatchQueue.main.async { [weak self] in
            guard let self else { return }
            self.stopPolling()
            self.pollTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [weak self] _ in
                self?.pollDiscoveredDevices()
            }
        }
    }

    private func startScanTimeout() {
        scanTimeoutTask?.cancel()
        scanTimeoutTask = Task { [weak self] in
            try? await Task.sleep(nanoseconds: 45_000_000_000)
            guard !Task.isCancelled, let self else { return }
            guard self.browser.isBrowsing else { return }
            guard self.notifiedUUIDs.isEmpty else { return }
            self.log("45s 内未发现 USB 相机")
            self.onScanTimeout?()
        }
    }

    private func pollDiscoveredDevices() {
        guard !isStopped else { return }
        pollTick += 1

        let devices = browser.devices ?? []
        if devices.isEmpty {
            emptyPollStreak += 1
        } else {
            emptyPollStreak = 0
        }

        if pollTick == 1 || pollTick % 10 == 0 {
            log(
                "轮询 tick=\(pollTick) devices.count=\(devices.count) " +
                    "isBrowsing=\(browser.isBrowsing) suspended=\(browser.isSuspended)"
            )
            for device in devices {
                logDeviceSummary(device, prefix: "  devices[]")
            }
        }

        // 连续 6 次（约 3s）无设备时自动重启 Browser；不与 reEnumerate 并发。
        if emptyPollStreak > 0, emptyPollStreak % 6 == 0, notifiedUUIDs.isEmpty, !isRestarting, !isReEnumerating {
            Task { await self.restart() }
        }

        for device in devices {
            handleDiscoveredDevice(device, source: "poll")
        }
    }

    private func handleDiscoveredDevice(_ device: ICDevice, source: String) {
        guard !isStopped else { return }
        guard let camera = device as? ICCameraDevice else {
            log(
                "[\(source)] 非 ICCameraDevice: \(device.name ?? "?") " +
                    "type=\(device.type) transport=\(device.transportType ?? "?")"
            )
            return
        }

        let uuid = camera.uuidString ?? "\(camera.usbVendorID)-\(camera.usbLocationID)"
        guard notifiedUUIDs.insert(uuid).inserted else { return }

        let vendorID = Int(camera.usbVendorID)
        log(
            "[\(source)] 发现相机: \(camera.name ?? "?") vid=0x\(String(vendorID, radix: 16)) " +
                "transport=\(camera.transportType ?? "?")"
        )
        scanTimeoutTask?.cancel()
        scanTimeoutTask = nil
        onCameraDiscovered?(camera, source)
    }

    private func requestAuthorizations(on browser: ICDeviceBrowser) async -> Bool {
        log(
            "contentsAuthorizationStatus=\(browser.contentsAuthorizationStatus), " +
                "controlAuthorizationStatus=\(browser.controlAuthorizationStatus)"
        )

        let contentsGranted = await withCheckedContinuation { continuation in
            browser.requestContentsAuthorization { status in
                CameraTetheringLogger.log("AutoDetect 内容授权: \(String(describing: status))")
                continuation.resume(returning: status == .authorized)
            }
        }
        guard contentsGranted else { return false }

        let controlGranted = await withCheckedContinuation { continuation in
            browser.requestControlAuthorization { status in
                CameraTetheringLogger.log("AutoDetect 控制授权: \(String(describing: status))")
                continuation.resume(returning: status == .authorized)
            }
        }
        if !controlGranted {
            log("控制授权未通过，仍尝试连接")
        }
        return true
    }

    private func logDeviceSummary(_ device: ICDevice, prefix: String) {
        if let camera = device as? ICCameraDevice {
            log(
                "\(prefix): \(camera.name ?? "?") vid=0x\(String(Int(camera.usbVendorID), radix: 16)) " +
                    "transport=\(camera.transportType ?? "?")"
            )
        } else {
            log("\(prefix): \(device.name ?? "?") type=\(device.type) transport=\(device.transportType ?? "?")")
        }
    }

    private func log(_ message: String) {
        CameraTetheringLogger.log("AutoDetect \(message)")
    }
}

extension USBDeviceBrowserController: ICDeviceBrowserDelegate {
    @objc(deviceBrowser:didAddDevice:moreComing:)
    func deviceBrowser(_ browser: ICDeviceBrowser, didAdd device: ICDevice, moreComing: Bool) {
        guard !isStopped else { return }
        log("didAdd 回调 name=\(device.name ?? "?") moreComing=\(moreComing)")
        handleDiscoveredDevice(device, source: "didAdd")
    }

    @objc(deviceBrowser:didRemoveDevice:moreGoing:)
    func deviceBrowser(_ browser: ICDeviceBrowser, didRemove device: ICDevice, moreGoing: Bool) {
        guard !isStopped else { return }
        log("didRemove 回调 name=\(device.name ?? "?") moreGoing=\(moreGoing)")
        if let uuid = device.uuidString {
            notifiedUUIDs.remove(uuid)
        }
        onCameraRemoved?(device)
    }

    func deviceBrowserDidSuspendOperations(_ browser: ICDeviceBrowser) {
        log("Browser 操作已挂起（App 进入后台）")
    }

    func deviceBrowserDidResumeOperations(_ browser: ICDeviceBrowser) {
        guard !isStopped else { return }
        log("Browser 操作已恢复（App 回到前台）")
        Task { await restart() }
    }
}
