import Foundation
import ImageCaptureCore

/// Canon EOS 相机有线连接服务，实现边拍边传与目录回退下载。
@MainActor
final class CanonCameraService: CameraServiceProtocol {
    let brand: CameraBrand = .canon

    private(set) var connectionState: CameraConnectionState = .disconnected
    var onConnectionStateChange: ((CameraConnectionState) -> Void)?
    var onNewAsset: ((CameraAsset) -> Void)?

    private let deviceManager = CanonImageCaptureDeviceManager()
    private var knownAssetIDs: Set<String> = []
    private var baselineEstablished = false
    private var pendingAssetsWhileDetached: [CameraAsset] = []

    init() {
        deviceManager.onConnectionStateChange = { [weak self] state in
            Task { @MainActor in
                self?.connectionState = state
                self?.onConnectionStateChange?(state)
            }
        }

        deviceManager.onNewItems = { [weak self] items in
            Task { @MainActor in
                self?.handleNewItems(items, source: "didAddItems")
            }
        }

        deviceManager.onBaselineCatalogReady = { [weak self] in
            Task { @MainActor in
                self?.establishBaseline()
            }
        }

        deviceManager.onPollTick = { [weak self] in
            Task { @MainActor in
                self?.scanForNewAssets(source: "poll")
            }
        }

        deviceManager.onPTPAssetReady = { [weak self] asset, _ in
            Task { @MainActor in
                self?.handlePTPAsset(asset)
            }
        }

        deviceManager.onError = { [weak self] message in
            Task { @MainActor in
                CameraTetheringLogger.log("Canon 相机错误: \(message)")
                self?.connectionState = .error(message)
                self?.onConnectionStateChange?(.error(message))
            }
        }
    }

    func connect() async {
        CameraTetheringLogger.log("CanonCameraService.connect()")
        let shouldResetBaseline = !deviceManager.isBrowsing
        deviceManager.startBrowsing()
        if shouldResetBaseline {
            knownAssetIDs.removeAll()
            baselineEstablished = false
        }
    }

    /// 由 AutoDetect 在已完成授权后直接挂载设备。
    func attach(to camera: ICCameraDevice) async {
        CameraTetheringLogger.log("CanonCameraService.attach()")
        knownAssetIDs.removeAll()
        baselineEstablished = false
        pendingAssetsWhileDetached.removeAll()
        deviceManager.attach(to: camera)
    }

    /// 页面离开：仅清空 UI 回调，底层 PTP 会话与轮询继续。
    func detachFromUI() {
        CameraTetheringLogger.log("CanonCameraService.detachFromUI()")
        onConnectionStateChange = nil
        onNewAsset = nil
    }

    /// 再次进入页面：补发 detach 期间缓冲的新照片。
    func reattachToUI() {
        guard !pendingAssetsWhileDetached.isEmpty else { return }
        let pending = pendingAssetsWhileDetached
        pendingAssetsWhileDetached.removeAll()
        CameraTetheringLogger.log("CanonCameraService.reattachToUI: 补发 \(pending.count) 张离线新片")
        for asset in pending {
            onNewAsset?(asset)
        }
    }

    func shutdown() async {
        CameraTetheringLogger.log("CanonCameraService.shutdown()")
        await deviceManager.stopBrowsing()
        knownAssetIDs.removeAll()
        baselineEstablished = false
        pendingAssetsWhileDetached.removeAll()
        connectionState = .disconnected
        onConnectionStateChange?(.disconnected)
    }

    func listAssets() async throws -> [CameraAsset] {
        guard connectionState.isConnected else {
            throw CameraServiceError.notConnected
        }
        return deviceManager.allFiles().map { deviceManager.makeAsset(from: $0) }
    }

    func downloadAsset(_ asset: CameraAsset) async throws -> URL {
        guard connectionState.isConnected else {
            throw CameraServiceError.notConnected
        }
        if let cachedURL = deviceManager.localURL(forAssetID: asset.id) {
            return cachedURL
        }
        guard let file = deviceManager.file(forAssetID: asset.id) else {
            throw CameraServiceError.assetNotFound
        }
        let directory = CameraDownloadStorage.temporaryDownloadsDirectory
        return try await deviceManager.download(file: file, to: directory)
    }

    private func handlePTPAsset(_ asset: CameraAsset) {
        deliverNewAsset(asset, source: "Canon PTP")
    }

    private func establishBaseline() {
        guard !baselineEstablished else { return }
        baselineEstablished = true

        let files = deviceManager.allFiles()
        var skipped = 0
        var newCount = 0

        for file in files {
            if file.wasAddedAfterContentCatalogCompleted {
                emitAssetIfNew(file: file, source: "baseline-new")
                newCount += 1
            } else {
                knownAssetIDs.insert(deviceManager.assetIdentifier(for: file))
                skipped += 1
            }
        }
        CameraTetheringLogger.log("Canon 基线已建立: 跳过已有 \(skipped) 张, 立即传输新拍 \(newCount) 张")
    }

    private func handleNewItems(_ items: [ICCameraItem], source: String) {
        guard baselineEstablished else { return }
        for item in items {
            guard let file = item as? ICCameraFile else { continue }
            emitAssetIfNew(file: file, source: source)
        }
    }

    private func scanForNewAssets(source: String) {
        guard baselineEstablished else { return }
        for file in deviceManager.allFiles() {
            emitAssetIfNew(file: file, source: source)
        }
    }

    private func emitAssetIfNew(file: ICCameraFile, source: String) {
        let asset = deviceManager.makeAsset(from: file)
        deliverNewAsset(asset, source: source)
    }

    private func deliverNewAsset(_ asset: CameraAsset, source: String) {
        guard !knownAssetIDs.contains(asset.id) else { return }
        knownAssetIDs.insert(asset.id)
        CameraTetheringLogger.log("[Canon \(source)] 发现新照片: \(asset.filename)", logger: CameraTetheringLogger.transfer)
        if let onNewAsset {
            onNewAsset(asset)
        } else {
            pendingAssetsWhileDetached.append(asset)
        }
    }
}
