//
//  TravelAlbumCameraImportViewModel.swift
//  suixinkan
//

import Foundation

/// 相机历史照片导入分组。
struct TravelAlbumCameraImportSection: Equatable {
    let dayKey: String
    let title: String
    let photos: [CameraObject]
}

/// 相机历史照片导入页状态。
enum TravelAlbumCameraImportState: Equatable {
    case idle
    case loading
    case loaded(sections: [TravelAlbumCameraImportSection])
    case importing(current: Int, total: Int)
    case finished(importedCount: Int, failedCount: Int, importedPhotoIds: [String])
    case failed(message: String)
}

/// 旅拍相册相机历史照片导入页 ViewModel，负责相机枚举、多选和本地导入。
@MainActor
final class TravelAlbumCameraImportViewModel {
    let albumId: Int
    let albumTitle: String

    private(set) var state: TravelAlbumCameraImportState = .idle {
        didSet {
            onStateUpdated?(state)
            onSonyMTPHelpVisibilityChanged?(shouldShowSonyMTPHelp)
        }
    }
    private(set) var selectedPhotoIds: Set<String> = [] {
        didSet { onSelectionUpdated?(selectedPhotoIds.count) }
    }
    private(set) var failedPhotoIds: Set<String> = []

    var onStateUpdated: ((TravelAlbumCameraImportState) -> Void)?
    var onSelectionUpdated: ((Int) -> Void)?
    var onSonyMTPHelpVisibilityChanged: ((Bool) -> Void)?

    private let driver: CameraDriver
    private let storage: TravelAlbumOTGPhotoStore
    private let appStore: AppStore
    private var allPhotos: [CameraObject] = []
    private var existingPhotoIds: Set<String> = []
    private var thumbnailCache: [String: Data] = [:]

    /// 初始化旅拍相册相机历史照片导入页 ViewModel。
    init(
        albumId: Int,
        albumTitle: String,
        driver: CameraDriver,
        storage: TravelAlbumOTGPhotoStore = TravelAlbumOTGPhotoStore(),
        appStore: AppStore = .shared
    ) {
        self.albumId = albumId
        self.albumTitle = albumTitle
        self.driver = driver
        self.storage = storage
        self.appStore = appStore
    }

    /// 索尼相机且目录为空时展示 MTP 帮助入口。
    var shouldShowSonyMTPHelp: Bool {
        guard driver.platform == .sony else { return false }
        switch state {
        case .loaded(let sections):
            return sections.isEmpty
        case .failed:
            return allPhotos.isEmpty
        default:
            return false
        }
    }

    /// 从相机读取历史照片并按日期分组。
    func loadPhotos() {
        state = .loading
        Task {
            do {
                reloadExistingPhotoIds()
                let objects = try await driver.listObjects()
                allPhotos = objects
                let sections = Self.groupByDay(objects)
                if sections.isEmpty {
                    state = .failed(message: "相机中没有可导入的照片")
                } else {
                    state = .loaded(sections: sections)
                }
            } catch {
                state = .failed(message: error.localizedDescription)
            }
        }
    }

    /// 是否已经导入当前相册。
    func isPhotoAlreadyInAlbum(id: String) -> Bool {
        existingPhotoIds.contains(id)
    }

    /// 切换单张照片选择状态。
    func togglePhoto(id: String) {
        guard !isPhotoAlreadyInAlbum(id: id) else { return }
        if selectedPhotoIds.contains(id) {
            selectedPhotoIds.remove(id)
        } else {
            selectedPhotoIds.insert(id)
        }
    }

    /// 切换整组照片选择状态。
    func toggleSection(_ section: TravelAlbumCameraImportSection) {
        let ids = Set(section.photos.filter { !isPhotoAlreadyInAlbum(id: $0.id) }.map(\.id))
        guard !ids.isEmpty else { return }
        if ids.isSubset(of: selectedPhotoIds) {
            selectedPhotoIds.subtract(ids)
        } else {
            selectedPhotoIds.formUnion(ids)
        }
    }

    /// 是否选中单张照片。
    func isPhotoSelected(id: String) -> Bool {
        selectedPhotoIds.contains(id)
    }

    /// 是否整组全选。
    func isSectionFullySelected(_ section: TravelAlbumCameraImportSection) -> Bool {
        let ids = section.photos.filter { !isPhotoAlreadyInAlbum(id: $0.id) }.map(\.id)
        return !ids.isEmpty && ids.allSatisfy { selectedPhotoIds.contains($0) }
    }

    /// 是否整组半选。
    func isSectionPartiallySelected(_ section: TravelAlbumCameraImportSection) -> Bool {
        let ids = section.photos.filter { !isPhotoAlreadyInAlbum(id: $0.id) }.map(\.id)
        let selectedCount = ids.filter { selectedPhotoIds.contains($0) }.count
        return selectedCount > 0 && selectedCount < ids.count
    }

    /// 分组是否还有可导入照片。
    func hasImportablePhotos(in section: TravelAlbumCameraImportSection) -> Bool {
        section.photos.contains { !isPhotoAlreadyInAlbum(id: $0.id) }
    }

    /// 分组可导入照片数。
    func importablePhotoCount(in section: TravelAlbumCameraImportSection) -> Int {
        section.photos.filter { !isPhotoAlreadyInAlbum(id: $0.id) }.count
    }

    /// 分组已导入照片数。
    func importedPhotoCount(in section: TravelAlbumCameraImportSection) -> Int {
        section.photos.filter { isPhotoAlreadyInAlbum(id: $0.id) }.count
    }

    /// 加载相机缩略图。
    func thumbnailData(for photo: CameraObject, maxPixelSize: Int) async -> Data? {
        if let cached = thumbnailCache[photo.id] {
            return cached
        }
        guard let data = await driver.requestThumbnailData(for: photo, maxPixelSize: maxPixelSize) else {
            return nil
        }
        thumbnailCache[photo.id] = data
        return data
    }

    /// 将已选照片导入当前相册本地缓存。
    func importSelected() {
        let selected = allPhotos.filter { selectedPhotoIds.contains($0.id) && !isPhotoAlreadyInAlbum(id: $0.id) }
        guard !selected.isEmpty else { return }

        state = .importing(current: 0, total: selected.count)
        failedPhotoIds = []

        Task {
            let directory = try? storage.originalsDirectory(albumId: albumId)
            var importedCount = 0
            var importedPhotoIds: [String] = []

            for (index, object) in selected.enumerated() {
                defer { state = .importing(current: index + 1, total: selected.count) }
                guard let directory else {
                    failedPhotoIds.insert(object.id)
                    continue
                }
                do {
                    let downloadedURL = try await driver.downloadObject(object, to: directory)
                    let record = TravelAlbumOTGPhotoRecord(
                        id: object.id,
                        sourceId: object.id,
                        fileName: downloadedURL.lastPathComponent,
                        localPath: storage.relativePath(for: downloadedURL, albumId: albumId),
                        thumbnailPath: "",
                        capturedAt: TravelAlbumOTGPhotoStore.transferTimeText(object.capturedAt),
                        fileSizeBytes: object.fileSize,
                        status: .pending,
                        progress: 0,
                        albumId: albumId,
                        userId: appStore.session.userId
                    )
                    storage.upsert(record, albumId: albumId)
                    existingPhotoIds.insert(object.id)
                    importedPhotoIds.append(record.id)
                    importedCount += 1
                } catch {
                    failedPhotoIds.insert(object.id)
                    OTGLog.error(.connection, "history import failed: \(object.filename) \(error.localizedDescription)")
                }
            }

            selectedPhotoIds.subtract(existingPhotoIds)
            state = .finished(
                importedCount: importedCount,
                failedCount: failedPhotoIds.count,
                importedPhotoIds: importedPhotoIds
            )
        }
    }

    private func reloadExistingPhotoIds() {
        existingPhotoIds = Set(
            storage.load(albumId: albumId).flatMap { record in
                [record.id, record.sourceId].filter { !$0.isEmpty }
            }
        )
    }

    private static func groupByDay(_ photos: [CameraObject]) -> [TravelAlbumCameraImportSection] {
        let calendar = Calendar.current
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        formatter.dateFormat = "yyyy年M月d日"
        let grouped = Dictionary(grouping: photos) { photo in
            calendar.startOfDay(for: photo.capturedAt)
        }
        return grouped.keys
            .sorted(by: >)
            .map { day in
                TravelAlbumCameraImportSection(
                    dayKey: dayKeyString(for: day, calendar: calendar),
                    title: sectionTitle(for: day, formatter: formatter, calendar: calendar),
                    photos: grouped[day]!.sorted { $0.capturedAt > $1.capturedAt }
                )
            }
    }

    private static func dayKeyString(for day: Date, calendar: Calendar) -> String {
        let components = calendar.dateComponents([.year, .month, .day], from: day)
        return String(format: "%04d-%02d-%02d", components.year ?? 0, components.month ?? 0, components.day ?? 0)
    }

    private static func sectionTitle(for day: Date, formatter: DateFormatter, calendar: Calendar) -> String {
        if calendar.isDateInToday(day) { return "今天" }
        if calendar.isDateInYesterday(day) { return "昨天" }
        return formatter.string(from: day)
    }
}
