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

import Foundation

/// 有线相机传输页 ViewModel，编排 ImageCaptureCore 连接、照片导入、本地缓存与上传登记。
@MainActor
final class WiredCameraTransferViewModel {
    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 { notifyStateChanged() }
    }
    private(set) var sonyMTPHint: String?
    private(set) var isContentCatalogReady = false

    let retouchOption = "不修图"
    private(set) var photoFormatOption: TravelAlbumOTGPhotoFormatOption = .jpg
    private(set) var transferModeOption = "边拍边传"

    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 appStore: AppStore

    private var currentDriver: CameraDriver?
    private var persistedRecordsById: [String: TravelAlbumOTGPhotoRecord] = [:]
    private var isUploading = false

    @MainActor
    init(
        albumId: Int,
        albumTitle: String,
        headerPhone: String,
        scenicSpotLabel: String? = nil,
        connectionManager: (any WiredCameraConnectionManaging)? = nil,
        storage: TravelAlbumOTGPhotoStore = TravelAlbumOTGPhotoStore(),
        uploader: (any TravelAlbumOTGUploading)? = nil,
        appStore: AppStore = .shared
    ) {
        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.appStore = appStore
    }

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

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

    /// 设备名称。
    var deviceModelName: String {
        switch connectionState {
        case .connecting(let name), .connected(_, let name):
            return name
        default:
            return "设备存储"
        }
    }

    /// 当前设备容量占位文案。
    var availableStorageText: String {
        "-- GB 可用"
    }

    /// 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
    }

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

    /// 页面消失时解绑 UI delegate，并暂停边拍边传轮询，保留底层设备缓存。
    func stop() {
        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 selectTransferMode(_ option: String) {
        guard option == "边拍边传" || option == "拍后传输" else { return }
        transferModeOption = option
    }

    /// 切换上传格式。
    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 onSpecifyUploadButtonClick() {
        let ids = Set(photos.filter(\.isNotUploaded).map(\.id))
        startUploadByIds(ids, emptyMessage: "暂无未上传的照片")
    }

    /// 创建相机历史照片选择导入页 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()
    }

    /// 选择或取消选择照片。
    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: "所选照片不可上传")
    }

    /// 重试上传单张照片。
    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 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) {
        guard !isUploading else {
            showMessage("正在上传，请稍候")
            return
        }
        let targets = resolveUploadTargets(ids)
        guard !targets.isEmpty else {
            showMessage(emptyMessage)
            return
        }
        isUploading = true
        showMessage("开始上传 \(targets.count) 张照片")
        Task {
            for id in targets {
                await uploadPhoto(id: id)
            }
            isUploading = false
            notifyStateChanged()
        }
    }

    private func resolveUploadTargets(_ ids: Set<String>) -> [String] {
        ids.filter { persistedRecordsById[$0] != nil }
    }

    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.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.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 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 filename: String, albumID: Int) {
        guard albumID == albumId else { return }
        loadPersistedPhotos()
        onLiveShotSaved?(filename)
    }
}
