//
//  WiredCameraTransferViewModel.swift
//  suixinkan
//

import Darwin
import Foundation

/// 手机相册导入到 OTG 本地缓存的图片数据。
struct TravelAlbumPhoneAlbumImportItem: Sendable, Equatable {
    let data: Data
    let fileName: String
    let createdAt: Date

    /// 创建手机相册导入图片。
    init(data: Data, fileName: String, createdAt: Date = Date()) {
        self.data = data
        self.fileName = fileName
        self.createdAt = createdAt
    }
}

/// 有线相机传输页 ViewModel，编排 ImageCaptureCore 连接、照片导入、本地缓存与上传登记。
@MainActor
final class WiredCameraTransferViewModel {
    private static let transferModeDefaultsKey = "travelAlbum.otg.transferMode"

    let albumId: Int
    let albumTitle: String
    let headerPhone: String
    let scenicSpotLabel: String?

    private(set) var connectionState: ConnectionState = .idle {
        didSet { notifyStateChanged() }
    }
    private(set) var photos: [TravelAlbumOTGPhotoItem] = [] {
        didSet {
            normalizeSelectedTimeSlot()
            notifyStateChanged()
        }
    }
    private(set) var selectedTab: TravelAlbumOTGTransferTab = .all {
        didSet {
            normalizeSelectedTimeSlot()
            notifyStateChanged()
        }
    }
    private(set) var selectedPhotoIds: Set<String> = [] {
        didSet { notifyStateChanged() }
    }
    private(set) var selectUploadMode = false {
        didSet { notifyStateChanged() }
    }
    private(set) var sidebarExpanded = true {
        didSet { notifyStateChanged() }
    }
    private(set) var selectedTimeSlotId: String? {
        didSet {
            guard !suppressSelectedTimeSlotNotification else { return }
            notifyStateChanged()
        }
    }
    private(set) var sonyMTPHint: String?
    private(set) var isContentCatalogReady = false

    let retouchOption = "不修图"
    private(set) var photoFormatOption: TravelAlbumOTGPhotoFormatOption = .jpg
    private(set) var transferMode: TravelAlbumOTGTransferMode = .liveUpload {
        didSet { notifyStateChanged() }
    }

    var onStateChange: (() -> Void)?
    var onShowMessage: ((String) -> Void)?
    var onLiveShotSaved: ((String) -> Void)?

    private let connectionManager: any WiredCameraConnectionManaging
    private let storage: TravelAlbumOTGPhotoStore
    private let repository: TravelAlbumOTGPhotoRepository
    private let uploader: any TravelAlbumOTGUploading
    private let api: any TravelAlbumServing
    private let appStore: AppStore
    private let userDefaults: UserDefaults

    private var currentDriver: CameraDriver?
    private var persistedRecordsById: [String: TravelAlbumOTGPhotoRecord] = [:]
    private var isUploading = false
    private var queuedAutoUploadPhotoIds: Set<String> = []
    private var suppressSelectedTimeSlotNotification = false
    private var serverStatusSyncTask: Task<Void, Never>?

    @MainActor
    init(
        albumId: Int,
        albumTitle: String,
        headerPhone: String,
        scenicSpotLabel: String? = nil,
        connectionManager: (any WiredCameraConnectionManaging)? = nil,
        storage: TravelAlbumOTGPhotoStore = TravelAlbumOTGPhotoStore(),
        uploader: (any TravelAlbumOTGUploading)? = nil,
        api: (any TravelAlbumServing)? = nil,
        appStore: AppStore = .shared,
        userDefaults: UserDefaults = .standard
    ) {
        self.albumId = albumId
        self.albumTitle = albumTitle.isEmpty ? "有线传输" : albumTitle
        self.headerPhone = headerPhone
        self.scenicSpotLabel = scenicSpotLabel
        self.connectionManager = connectionManager ?? ConnectionManager.shared
        self.storage = storage
        self.repository = TravelAlbumOTGPhotoRepository(storage: storage)
        self.uploader = uploader ?? TravelAlbumOTGUploader()
        self.api = api ?? NetworkServices.shared.travelAlbumAPI
        self.appStore = appStore
        self.userDefaults = userDefaults
        self.transferMode = Self.persistedTransferMode(in: userDefaults)
    }

    /// 相机状态文案。
    var cameraStatusText: String {
        switch connectionState {
        case .idle, .disconnected:
            return "未连接 USB"
        case .searching:
            return "正在搜索"
        case .connecting:
            return "正在连接"
        case .connected(_, let name):
            return "\(name)已连接"
        case .failed:
            return "连接失败"
        }
    }

    /// 操作按钮文案。
    var actionButtonText: String {
        switch connectionState {
        case .idle, .disconnected, .failed:
            return "重新连接"
        case .searching, .connecting:
            return "等待连接"
        case .connected:
            return "刷新"
        }
    }

    /// 当前手机型号名称。
    var deviceModelName: String {
        Self.currentPhoneModelName()
    }

    /// 当前手机可用存储空间。
    var availableStorageText: String {
        Self.availableStorageText()
    }

    /// 是否已连接相机。
    var isCameraConnected: Bool {
        if case .connected = connectionState {
            return true
        }
        return false
    }

    /// Tab 统计。
    var tabCounts: [Int] {
        [
            photos.count,
            photos.filter { $0.status == .uploaded }.count,
            photos.filter { $0.status == .failed }.count,
        ]
    }

    /// 过滤后的照片列表。
    func filteredPhotos() -> [TravelAlbumOTGPhotoItem] {
        switch selectedTab {
        case .all:
            return photos
        case .uploaded:
            return photos.filter { $0.status == .uploaded }
        case .failed:
            return photos.filter { $0.status == .failed }
        }
    }

    /// 过滤后照片的半小时时间分段。
    var photoSections: [TravelAlbumOTGPhotoSection] {
        filteredPhotos().buildPhotoSections()
    }

    /// 过滤后照片的左侧时间导航分组。
    var sidebarGroups: [TravelAlbumOTGDateGroup] {
        filteredPhotos().buildSidebarGroups()
    }

    /// 当前可见列表中可被选择上传的照片 ID。
    var selectableVisiblePhotoIds: Set<String> {
        Set(filteredPhotos().filter(\.canSelectForUpload).map(\.id))
    }

    /// 当前可见列表中是否已全选可上传照片。
    var allVisibleSelectablePhotosSelected: Bool {
        let ids = selectableVisiblePhotoIds
        return !ids.isEmpty && ids.isSubset(of: selectedPhotoIds)
    }

    /// 当前可见列表中已选择的可上传照片数量。
    var selectedVisibleUploadCount: Int {
        selectedPhotoIds.intersection(selectableVisiblePhotoIds).count
    }

    /// 传输模式展示文案。
    var transferModeOption: String {
        transferMode.title
    }

    /// 指定上传弹窗选项。
    var specifyUploadOptions: [TravelAlbumOTGSpecifyUploadOption] {
        TravelAlbumOTGSpecifyUploadOption.allCases
    }

    /// 是否可打开指定上传选项。
    var canOpenSpecifyUploadChooser: Bool {
        !selectUploadMode
    }

    /// 绑定连接管理器并启动发现。
    func start() {
        connectionManager.configureLiveTransfer(albumID: albumId)
        connectionManager.delegate = self
        syncFromConnectionManager()
        loadPersistedPhotos()
        syncServerUploadStatuses()
        connectionManager.start()
    }

    /// 页面消失时解绑 UI delegate，并暂停边拍边传轮询，保留底层设备缓存。
    func stop() {
        serverStatusSyncTask?.cancel()
        serverStatusSyncTask = nil
        connectionManager.unbindDelegate()
        connectionManager.suspendLiveTransfer()
    }

    /// 主动断开相机连接。
    func disconnect() {
        connectionManager.disconnect()
    }

    /// 刷新相机文件列表或重新连接。
    func refreshCameraFiles() {
        switch connectionState {
        case .connected:
            loadPersistedPhotos()
        case .idle, .disconnected, .failed:
            connectionManager.start()
        default:
            break
        }
    }

    /// 重新加载当前相册本地 OTG 照片。
    func reloadLocalPhotos() {
        loadPersistedPhotos()
    }

    /// 切换 Tab。
    func selectTab(_ tab: TravelAlbumOTGTransferTab) {
        selectedTab = tab
        selectedPhotoIds = selectedPhotoIds.intersection(Set(filteredPhotos().map(\.id)))
    }

    /// 展开或收起左侧时间快速导航。
    func toggleSidebar() {
        sidebarExpanded.toggle()
    }

    /// 选择左侧时间槽，并联动主列表滚动。
    func selectTimeSlot(id: String) {
        guard photoSections.contains(where: { $0.slotId == id }) else { return }
        selectedTimeSlotId = id
    }

    /// 同步主列表当前可见时间槽，只更新左侧时间轴选中态，不触发整页重新绑定。
    func syncVisibleTimeSlot(id: String) {
        guard selectedTimeSlotId != id,
              photoSections.contains(where: { $0.slotId == id }) else {
            return
        }
        suppressSelectedTimeSlotNotification = true
        selectedTimeSlotId = id
        suppressSelectedTimeSlotNotification = false
    }

    /// 切换传输模式。
    func selectTransferMode(_ option: String) {
        guard let mode = TravelAlbumOTGTransferMode.option(title: option),
              mode != transferMode else {
            return
        }
        transferMode = mode
        userDefaults.set(mode.rawValue, forKey: Self.transferModeDefaultsKey)
    }

    /// 切换上传格式。
    func selectPhotoFormat(_ option: TravelAlbumOTGPhotoFormatOption) {
        guard photoFormatOption != option else { return }
        photoFormatOption = option
        applyMergedPhotos()
    }

    /// 批量上传按钮：首次进入选择模式，再次上传选中照片。
    func onBatchUploadButtonClick() {
        if !selectUploadMode {
            selectUploadMode = true
            selectedPhotoIds = []
            return
        }
        if selectedPhotoIds.isEmpty {
            selectUploadMode = false
            return
        }
        uploadSelectedTransferPhotos()
    }

    /// 选择指定上传选项后开始上传。
    func onSpecifyUploadOptionSelected(_ option: TravelAlbumOTGSpecifyUploadOption) {
        guard canOpenSpecifyUploadChooser else { return }
        switch option {
        case .allPending:
            uploadAllNotUploadedPhotos()
        case .todayCaptured:
            uploadAllTodayCapturedPhotos()
        }
    }

    /// 创建相机历史照片选择导入页 ViewModel。
    func makeCameraImportViewModel() -> TravelAlbumCameraImportViewModel? {
        guard isContentCatalogReady else {
            showMessage("相机照片目录尚未就绪，请稍候再试")
            return nil
        }
        guard let currentDriver else {
            showMessage("请先连接相机")
            return nil
        }
        if case .connected = connectionState {
            return TravelAlbumCameraImportViewModel(
                albumId: albumId,
                albumTitle: albumTitle,
                driver: currentDriver,
                storage: storage,
                appStore: appStore
            )
        }
        showMessage("请先连接相机")
        return nil
    }

    /// 历史导入期间暂停边拍边传轮询，避免 PTP 轮询与 ImageCaptureCore 下载互相抢占。
    func pauseLiveTransferForHistoricalImport() {
        connectionManager.suspendLiveTransfer()
    }

    /// 处理本次新传入 App 的照片，按当前传输模式决定是否自动上传。
    func handleNewlyImportedPhotoIds(_ photoIds: [String]) {
        let ids = Set(photoIds)
        loadPersistedPhotos()
        guard !ids.isEmpty, transferMode.shouldAutoUploadNewImports else { return }
        startUploadByIds(
            ids,
            emptyMessage: "暂无可自动上传的照片",
            queueIfBusy: true
        )
    }

    /// 将手机相册选择的图片导入当前 OTG 相册本地缓存。
    func importPhoneAlbumImages(_ items: [TravelAlbumPhoneAlbumImportItem]) -> (importedCount: Int, failedCount: Int, importedPhotoIds: [String]) {
        guard !items.isEmpty else { return (0, 0, []) }
        var importedPhotoIds: [String] = []
        var failedCount = 0

        for item in items {
            do {
                let fileURL = try storage.writeImage(item.data, filename: item.fileName, albumId: albumId)
                let record = TravelAlbumOTGPhotoRecord(
                    id: "phone_album_\(UUID().uuidString)",
                    fileName: fileURL.lastPathComponent,
                    localPath: storage.relativePath(for: fileURL, albumId: albumId),
                    capturedAt: TravelAlbumOTGPhotoStore.transferTimeText(item.createdAt),
                    fileSizeBytes: Int64(item.data.count),
                    status: .pending,
                    albumId: albumId,
                    userId: appStore.session.userId
                )
                storage.upsert(record, albumId: albumId)
                importedPhotoIds.append(record.id)
            } catch {
                failedCount += 1
                OTGLog.error(.connection, "phone album import failed: \(item.fileName) \(error.localizedDescription)")
            }
        }

        handleNewlyImportedPhotoIds(importedPhotoIds)
        return (importedPhotoIds.count, failedCount, importedPhotoIds)
    }

    /// 选择或取消选择照片。
    func toggleTransferPhotoSelection(photoId: String) {
        guard selectUploadMode,
              photos.first(where: { $0.id == photoId })?.canSelectForUpload == true else {
            return
        }
        if selectedPhotoIds.contains(photoId) {
            selectedPhotoIds.remove(photoId)
        } else {
            selectedPhotoIds.insert(photoId)
        }
    }

    /// 切换当前可见照片的全选状态。
    func toggleVisibleSelectablePhotos() {
        guard selectUploadMode else { return }
        let ids = selectableVisiblePhotoIds
        guard !ids.isEmpty else { return }
        if ids.isSubset(of: selectedPhotoIds) {
            selectedPhotoIds.subtract(ids)
        } else {
            selectedPhotoIds.formUnion(ids)
        }
    }

    /// 上传选中照片。
    func uploadSelectedTransferPhotos() {
        let ids = selectedPhotoIds
        guard !ids.isEmpty else {
            showMessage("请先选择照片")
            return
        }
        selectUploadMode = false
        selectedPhotoIds = []
        startUploadByIds(ids, emptyMessage: "所选照片不可上传")
    }

    private func uploadAllNotUploadedPhotos() {
        let ids = Set(photos.filter(\.isNotUploaded).map(\.id))
        startUploadByIds(ids, emptyMessage: "暂无未上传的照片")
    }

    private func uploadAllTodayCapturedPhotos() {
        let ids = Set(photos.filter { photo in
            guard photo.isNotUploaded,
                  let capturedDate = photo.capturedDate() else {
                return false
            }
            return Calendar.current.isDateInToday(capturedDate)
        }.map(\.id))
        startUploadByIds(ids, emptyMessage: "暂无今日拍摄且未上传的照片")
    }

    /// 重试上传单张照片。
    func retryPhoto(photoId: String) {
        startUploadByIds([photoId], emptyMessage: "本地文件不存在，无法重传")
    }

    /// 删除单张本地照片。
    func deletePhoto(photoId: String) {
        storage.remove(albumId: albumId, photoId: photoId)
        persistedRecordsById.removeValue(forKey: photoId)
        selectedPhotoIds.remove(photoId)
        applyMergedPhotos()
        showMessage("已删除")
    }

    /// 清空当前相册本地 OTG 缓存。
    func clearLocalAlbumCache() {
        storage.clearAlbum(albumId: albumId)
        persistedRecordsById = [:]
        photos = []
    }

    private func syncFromConnectionManager() {
        currentDriver = connectionManager.currentDriver
        isContentCatalogReady = connectionManager.isContentCatalogReady
        connectionState = connectionManager.state
    }

    private func loadPersistedPhotos() {
        let records = storage.load(albumId: albumId)
        persistedRecordsById = Dictionary(uniqueKeysWithValues: records.map { ($0.id, $0) })
        applyMergedPhotos()
    }

    private func syncServerUploadStatuses() {
        serverStatusSyncTask?.cancel()
        guard albumId > 0 else { return }
        let baseline = persistedRecordsById
        serverStatusSyncTask = Task { [weak self] in
            guard let self else { return }
            do {
                let response = try await api.materialClientPhotoIds(userEquityTravelId: albumId)
                try Task.checkCancellation()
                let activeIds = Set(persistedRecordsById.values.compactMap { record in
                    record.status == .transferring || record.status == .uploading ? record.id : nil
                })
                let reconciled = TravelAlbumOTGServerStatusPolicy.reconcile(
                    records: Array(persistedRecordsById.values),
                    baselineRecordsById: baseline,
                    activePhotoIds: activeIds,
                    serverClientPhotoIds: response.clientPhotoIds,
                    now: Int64(Date().timeIntervalSince1970 * 1000)
                )
                let reconciledById = Dictionary(uniqueKeysWithValues: reconciled.map { ($0.id, $0) })
                guard reconciledById != persistedRecordsById else { return }
                persistedRecordsById = reconciledById
                storage.save(reconciled, albumId: albumId)
                applyMergedPhotos()
                selectedPhotoIds = selectedPhotoIds.intersection(selectableVisiblePhotoIds)
            } catch is CancellationError {
                return
            } catch {
                OTGLog.error(.connection, "sync server upload statuses failed: \(error.localizedDescription)")
            }
        }
    }

    private func applyMergedPhotos() {
        let persistedItems = persistedRecordsById.values
            .map { $0.toPhotoItem(storage: storage, albumId: albumId) }
        photos = persistedItems.sorted { $0.capturedAt > $1.capturedAt }
    }

    private func normalizeSelectedTimeSlot() {
        let sections = filteredPhotos().buildPhotoSections()
        guard let firstSlotId = sections.first?.slotId else {
            selectedTimeSlotId = nil
            return
        }
        if selectedTimeSlotId == nil || !sections.contains(where: { $0.slotId == selectedTimeSlotId }) {
            selectedTimeSlotId = firstSlotId
        }
    }

    private func startUploadByIds(
        _ ids: Set<String>,
        emptyMessage: String,
        queueIfBusy: Bool = false
    ) {
        guard !isUploading else {
            if queueIfBusy {
                queuedAutoUploadPhotoIds.formUnion(ids)
            } else {
                showMessage("正在上传，请稍候")
            }
            return
        }
        let targets = resolveUploadTargets(ids)
        guard !targets.isEmpty else {
            showMessage(emptyMessage)
            return
        }
        isUploading = true
        Task {
            for id in targets {
                await uploadPhoto(id: id)
            }
            isUploading = false
            notifyStateChanged()
            uploadQueuedAutoImportsIfNeeded()
        }
    }

    private func uploadQueuedAutoImportsIfNeeded() {
        let ids = queuedAutoUploadPhotoIds
        queuedAutoUploadPhotoIds = []
        guard !ids.isEmpty else { return }
        startUploadByIds(
            ids,
            emptyMessage: "暂无可自动上传的照片",
            queueIfBusy: true
        )
    }

    private func resolveUploadTargets(_ ids: Set<String>) -> [String] {
        ids.filter { id in
            guard let record = persistedRecordsById[id] else { return false }
            return record.status == .pending || record.status == .failed
        }
    }

    private func uploadPhoto(id: String) async {
        do {
            let record = try await localRecordForUpload(id: id)
            updateRecord(id: id, status: .uploading, progress: max(record.progress, 1), error: nil)
            let material = try await uploader.upload(
                record: record,
                scenicId: appStore.session.currentScenicId
            ) { [weak self] progress in
                self?.updateRecord(id: id, status: .uploading, progress: progress, error: nil)
            }
            updateRecord(id: id, status: .uploaded, progress: 100, error: nil, remoteUrl: material.fileUrl)
        } catch {
            updateRecord(id: id, status: .failed, progress: 0, error: error.localizedDescription)
            showMessage(error.localizedDescription)
        }
    }

    private func localRecordForUpload(id: String) async throws -> TravelAlbumOTGPhotoRecord {
        if let record = persistedRecordsById[id],
           !record.localPath.isEmpty,
           storage.fileExists(relativePath: record.localPath, albumId: albumId) {
            return storage.recordWithResolvedFilePaths(record, albumId: albumId)
        }
        throw TravelAlbumOTGUploadError.localFileMissing
    }

    private func updateRecord(
        id: String,
        status: TravelAlbumOTGUploadStatus,
        progress: Int,
        error: String?,
        remoteUrl: String? = nil
    ) {
        var record = persistedRecordsById[id]
        if record == nil, let item = photos.first(where: { $0.id == id }) {
            record = TravelAlbumOTGPhotoRecord(
                id: item.id,
                sourceId: item.sourceId,
                fileName: item.fileName,
                localPath: item.localPath,
                thumbnailPath: item.thumbnailURL?.isFileURL == true
                    ? storage.relativePath(for: item.thumbnailURL ?? URL(fileURLWithPath: ""), albumId: albumId)
                    : "",
                capturedAt: item.capturedAt,
                fileSizeBytes: item.fileSizeBytes,
                status: status,
                progress: progress,
                errorMessage: error,
                albumId: albumId,
                userId: appStore.session.userId,
                remoteUrl: remoteUrl ?? item.remoteUrl
            )
        }
        guard var record else { return }
        record.status = status
        record.progress = min(max(progress, 0), 100)
        record.errorMessage = error
        if let remoteUrl {
            record.remoteUrl = remoteUrl
        }
        record.updatedAt = Int64(Date().timeIntervalSince1970 * 1000)
        persistedRecordsById[id] = record
        storage.upsert(record, albumId: albumId)
        applyMergedPhotos()
    }

    private func notifyStateChanged() {
        if Thread.isMainThread {
            onStateChange?()
        } else {
            DispatchQueue.main.async { [weak self] in self?.onStateChange?() }
        }
    }

    private static func persistedTransferMode(in userDefaults: UserDefaults) -> TravelAlbumOTGTransferMode {
        guard let rawValue = userDefaults.string(forKey: transferModeDefaultsKey),
              let mode = TravelAlbumOTGTransferMode(rawValue: rawValue) else {
            return .liveUpload
        }
        return mode
    }

    private static func currentPhoneModelName() -> String {
        let identifier = machineIdentifier()
        if identifier == "i386" || identifier == "x86_64" || identifier == "arm64" {
            return "iPhone 模拟器"
        }
        if let modelName = iPhoneModelNames[identifier] {
            return modelName
        }
        return identifier.hasPrefix("iPhone") ? identifier : "iPhone"
    }

    private static func machineIdentifier() -> String {
        var systemInfo = utsname()
        uname(&systemInfo)
        return withUnsafePointer(to: &systemInfo.machine) { pointer in
            pointer.withMemoryRebound(to: CChar.self, capacity: 1) {
                String(validatingUTF8: $0) ?? "iPhone"
            }
        }
    }

    private static func availableStorageText() -> String {
        guard let bytes = availableStorageBytes() else {
            return "-- GB 可用"
        }
        let gigabytes = Double(bytes) / 1_073_741_824.0
        if gigabytes >= 100 {
            return "\(Int(gigabytes.rounded(.down))) GB 可用"
        }
        return String(format: "%.1f GB 可用", gigabytes)
    }

    private static func availableStorageBytes() -> Int64? {
        let homeURL = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
        if let values = try? homeURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey]),
           let capacity = values.volumeAvailableCapacityForImportantUsage {
            return capacity
        }
        if let attributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()),
           let freeSize = attributes[.systemFreeSize] as? NSNumber {
            return freeSize.int64Value
        }
        return nil
    }

    private static let iPhoneModelNames: [String: String] = [
        "iPhone12,1": "iPhone 11",
        "iPhone12,3": "iPhone 11 Pro",
        "iPhone12,5": "iPhone 11 Pro Max",
        "iPhone12,8": "iPhone SE 2",
        "iPhone13,1": "iPhone 12 mini",
        "iPhone13,2": "iPhone 12",
        "iPhone13,3": "iPhone 12 Pro",
        "iPhone13,4": "iPhone 12 Pro Max",
        "iPhone14,2": "iPhone 13 Pro",
        "iPhone14,3": "iPhone 13 Pro Max",
        "iPhone14,4": "iPhone 13 mini",
        "iPhone14,5": "iPhone 13",
        "iPhone14,6": "iPhone SE 3",
        "iPhone14,7": "iPhone 14",
        "iPhone14,8": "iPhone 14 Plus",
        "iPhone15,2": "iPhone 14 Pro",
        "iPhone15,3": "iPhone 14 Pro Max",
        "iPhone15,4": "iPhone 15",
        "iPhone15,5": "iPhone 15 Plus",
        "iPhone16,1": "iPhone 15 Pro",
        "iPhone16,2": "iPhone 15 Pro Max",
        "iPhone17,1": "iPhone 16 Pro",
        "iPhone17,2": "iPhone 16 Pro Max",
        "iPhone17,3": "iPhone 16",
        "iPhone17,4": "iPhone 16 Plus",
        "iPhone17,5": "iPhone 16e",
    ]

    private func showMessage(_ message: String) {
        if Thread.isMainThread {
            onShowMessage?(message)
        } else {
            DispatchQueue.main.async { [weak self] in self?.onShowMessage?(message) }
        }
    }

}

extension WiredCameraTransferViewModel: ConnectionManagerDelegate {
    func connectionManager(_ manager: ConnectionManager, didUpdate state: ConnectionState) {
        connectionState = state
    }

    func connectionManager(_ manager: ConnectionManager, didCreate driver: any CameraDriver) {
        currentDriver = driver
    }

    func connectionManager(_ manager: ConnectionManager, contentCatalogDidBecomeReady driver: any CameraDriver) {
        currentDriver = driver
        isContentCatalogReady = true
        notifyStateChanged()
    }

    func connectionManager(_ manager: ConnectionManager, didFail error: any Error) {
        showMessage(error.localizedDescription)
    }

    func connectionManagerDidSuggestReplug(_ manager: ConnectionManager) {
        showMessage("长时间未找到相机，请尝试重新插拔连接线")
    }

    func connectionManager(_ manager: ConnectionManager, didSaveLiveShot record: TravelAlbumOTGPhotoRecord) {
        guard record.albumId == albumId else { return }
        handleNewlyImportedPhotoIds([record.id])
        onLiveShotSaved?(record.fileName)
    }
}
