//
//  ScenicApplicationViewModel.swift
//  suixinkan
//

import Foundation

/// 景区申请图片上传协议，便于单元测试注入 mock。
@MainActor
protocol ScenicApplicationImageUploading: AnyObject {
    func uploadScenicApplyImage(
        data: Data,
        fileName: String,
        scenicId: Int,
        onProgress: @escaping @MainActor (Int) -> Void
    ) async throws -> String
}

extension OSSUploadService: ScenicApplicationImageUploading {}

/// 景区申请 ViewModel，对齐 Android `ScenicApplicationViewModel`。
final class ScenicApplicationViewModel {

    private struct UploadBatch {
        let id: UUID
        var indices: [Int]
        var position: Int = 0
        var totalCount: Int
        var completedCount: Int = 0
    }

    private(set) var scenicName = ""
    private(set) var selectedImages: [ScenicApplicationImageItem] = []
    private(set) var selectedProvince = ""
    private(set) var selectedCity = ""
    private(set) var selectedCooperationType: ScenicCooperationType = .photographer
    private(set) var remarks = ""
    private(set) var isAgreed = false
    private(set) var isSubmitting = false
    private(set) var provinceList: [AreasResponse] = []
    private(set) var cityList: [AreasResponse] = []
    private(set) var pendingApplication: ScenicApplicationPendingResponse?
    private(set) var uploadDialogState: ScenicApplicationUploadDialogState?

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

    private var uploadBatchQueue: [UploadBatch] = []
    private var currentBatch: UploadBatch?
    private var uploadingIndex: Int?
    private var isProcessingUploadQueue = false
    private var lastProgressUpdateTime: TimeInterval = 0
    private let progressUpdateInterval: TimeInterval = 0.2

    /// 是否满足提交条件，对齐 Android `ScenicApplicationUiState.canSubmit`。
    var canSubmit: Bool {
        !selectedProvince.isEmpty
            && !selectedCity.isEmpty
            && !remarks.isEmpty
            && remarks.count <= 50
            && isAgreed
            && !isSubmitting
    }

    /// status == 1 时为只读。
    var isReadOnly: Bool {
        pendingApplication?.status == 1
    }

    /// 是否可编辑表单。
    var isEditable: Bool {
        guard let pending = pendingApplication else { return true }
        return pending.status == 2 || pending.status == 3 || pending.status == 9
    }

    /// 是否展示审核状态卡片（status 1/3/9）。
    var shouldShowAuditCard: Bool {
        guard let pending = pendingApplication else { return false }
        return pending.status == 1 || pending.status == 3 || pending.status == 9
    }

    /// 初始化加载省市与待审核数据。
    func loadInitial(profileAPI: ProfileAPI, homeAPI: HomeAPI) async {
        async let areasTask: Void = loadAreas(profileAPI: profileAPI)
        async let pendingTask: Void = loadPendingApplication(homeAPI: homeAPI)
        _ = await (areasTask, pendingTask)
    }

    func loadAreas(profileAPI: ProfileAPI) async {
        do {
            provinceList = try await profileAPI.areas()
            notifyStateChange()
        } catch {
            onShowMessage?("获取省市数据失败: \(error.localizedDescription)")
        }
    }

    func loadPendingApplication(homeAPI: HomeAPI) async {
        do {
            let response = try await homeAPI.scenicApplicationPending()
            guard let pending = response.items.first else {
                pendingApplication = nil
                notifyStateChange()
                return
            }
            if pending.status == 1 || pending.status == 3 {
                pendingApplication = pending
                await waitForProvinceListIfNeeded()
                initWithPendingApplication(pending)
            } else {
                pendingApplication = nil
            }
            notifyStateChange()
        } catch {
            pendingApplication = nil
            notifyStateChange()
        }
    }

    func onScenicNameChange(_ name: String) {
        scenicName = name
        notifyStateChange()
    }

    func onProvinceSelected(_ provinceName: String) {
        let province = provinceList.first { $0.name == provinceName }
        selectedProvince = provinceName
        selectedCity = ""
        cityList = province?.children ?? []
        notifyStateChange()
    }

    func onCitySelected(_ cityName: String) {
        selectedCity = cityName
        notifyStateChange()
    }

    func onCooperationTypeSelected(_ type: ScenicCooperationType) {
        selectedCooperationType = type
        notifyStateChange()
    }

    func onRemarksChange(_ text: String) {
        if text.count <= 50 {
            remarks = text
            notifyStateChange()
        } else {
            onShowMessage?("备注信息最多50字")
        }
    }

    func toggleAgreement() {
        isAgreed.toggle()
        notifyStateChange()
    }

    /// 添加本地图片并排队上传。
    func addLocalImages(_ items: [ScenicApplicationImageItem]) {
        guard !items.isEmpty else { return }
        let startIndex = selectedImages.count
        selectedImages.append(contentsOf: items)
        let indices = (0..<items.count).map { startIndex + $0 }
        uploadBatchQueue.append(
            UploadBatch(id: UUID(), indices: indices, totalCount: indices.count)
        )
        notifyStateChange()
    }

    func deleteImage(at index: Int) {
        guard index >= 0, index < selectedImages.count else { return }
        selectedImages.remove(at: index)
        adjustBatchAfterDeletion(deletedIndex: index)
        if uploadingIndex == index {
            uploadingIndex = nil
            uploadDialogState = nil
        } else if let uploadingIndex, index < uploadingIndex {
            self.uploadingIndex = uploadingIndex - 1
        }
        notifyStateChange()
    }

    func retryUpload(at index: Int) {
        guard index >= 0, index < selectedImages.count else { return }
        selectedImages[index].errorMessage = nil
        uploadBatchQueue.insert(
            UploadBatch(id: UUID(), indices: [index], totalCount: 1),
            at: 0
        )
        notifyStateChange()
    }

    /// 顺序处理待上传图片队列。
    func processPendingUploads(uploader: ScenicApplicationImageUploading, scenicId: Int) async {
        guard !isProcessingUploadQueue else { return }
        isProcessingUploadQueue = true
        defer { isProcessingUploadQueue = false }

        while true {
            if currentBatch == nil || (currentBatch?.position ?? 0) >= (currentBatch?.indices.count ?? 0) {
                currentBatch = uploadBatchQueue.isEmpty ? nil : uploadBatchQueue.removeFirst()
            }
            guard var batch = currentBatch else {
                uploadDialogState = nil
                notifyStateChange()
                return
            }

            while batch.position < batch.indices.count {
                let index = batch.indices[batch.position]
                guard index < selectedImages.count else {
                    batch.indices.remove(at: batch.position)
                    batch.totalCount = max(batch.completedCount, batch.totalCount - 1)
                    continue
                }

                let image = selectedImages[index]
                if image.isUploaded {
                    batch.position += 1
                    batch.completedCount = min(batch.totalCount, batch.completedCount + 1)
                    continue
                }
                if image.errorMessage != nil {
                    batch.position += 1
                    continue
                }
                guard let data = image.localData else {
                    batch.position += 1
                    continue
                }

                currentBatch = batch
                uploadingIndex = index
                updateImage(at: index) { item in
                    var copy = item
                    copy.isUploading = true
                    copy.uploadProgress = 0
                    copy.errorMessage = nil
                    return copy
                }
                let total = max(batch.totalCount, 1)
                let currentPosition = min(batch.completedCount + 1, total)
                uploadDialogState = ScenicApplicationUploadDialogState(
                    title: "正在上传(\(currentPosition)/\(total))",
                    progress: 0
                )
                notifyStateChange()

                do {
                    let progressBatch = batch
                    let url = try await uploader.uploadScenicApplyImage(
                        data: data,
                        fileName: image.fileName,
                        scenicId: scenicId,
                        onProgress: { [weak self] progress in
                            self?.handleUploadProgress(
                                index: index,
                                batch: progressBatch,
                                currentPosition: currentPosition,
                                total: total,
                                progress: progress
                            )
                        }
                    )
                    updateImage(at: index) { item in
                        var copy = item
                        copy.isUploading = false
                        copy.uploadProgress = 100
                        copy.remoteURL = url
                        copy.errorMessage = nil
                        return copy
                    }
                    batch.completedCount = min(batch.totalCount, batch.completedCount + 1)
                } catch {
                    updateImage(at: index) { item in
                        var copy = item
                        copy.isUploading = false
                        copy.uploadProgress = 0
                        copy.errorMessage = error.localizedDescription.isEmpty ? "上传失败" : error.localizedDescription
                        return copy
                    }
                    onShowMessage?("图片上传失败，请重试")
                }

                batch.position += 1
                uploadingIndex = nil
                currentBatch = batch
            }

            currentBatch = nil
            uploadDialogState = nil
            notifyStateChange()
        }
    }

    func submit(homeAPI: HomeAPI) async {
        guard canSubmit else {
            onShowMessage?("请完善必填信息")
            return
        }
        if selectedImages.contains(where: \.isUploading) {
            onShowMessage?("图片正在上传，请稍后提交")
            return
        }
        if selectedImages.contains(where: { !$0.isUploaded && $0.errorMessage == nil && $0.localData != nil }) {
            onShowMessage?("请先完成图片上传")
            return
        }

        isSubmitting = true
        notifyStateChange()
        defer {
            isSubmitting = false
            notifyStateChange()
        }

        let uploadedURLs = selectedImages.compactMap { item -> String? in
            item.isUploaded ? item.remoteURL : nil
        }
        let request = ScenicApplicationSubmitRequest(
            scenicName: scenicName.isEmpty ? nil : scenicName,
            scenicImages: uploadedURLs.isEmpty ? nil : uploadedURLs,
            scenicProvince: selectedProvince,
            scenicCity: selectedCity,
            coopType: selectedCooperationType.rawValue,
            remark: remarks,
            scenicId: 0
        )

        do {
            try await homeAPI.submitScenicApplication(request)
            onShowMessage?("提交成功")
            await loadPendingApplication(homeAPI: homeAPI)
            onSubmitSuccess?()
        } catch {
            onShowMessage?("提交失败: \(error.localizedDescription)")
        }
    }

    private func initWithPendingApplication(_ pending: ScenicApplicationPendingResponse) {
        let imageStates = pending.scenicImages
            .split(separator: ",")
            .map { String($0).trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }
            .map { ScenicApplicationImageItem.fromRemoteURL($0) }

        let province = provinceList.first { $0.name == pending.scenicProvince }
        scenicName = pending.scenicName
        selectedImages = imageStates
        selectedProvince = pending.scenicProvince
        selectedCity = pending.scenicCity
        selectedCooperationType = ScenicCooperationType.from(rawValue: pending.coopType)
        remarks = pending.remark ?? ""
        cityList = province?.children ?? []
    }

    private func waitForProvinceListIfNeeded() async {
        var waitCount = 0
        while provinceList.isEmpty && waitCount < 30 {
            try? await Task.sleep(nanoseconds: 100_000_000)
            waitCount += 1
        }
    }

    private func handleUploadProgress(
        index: Int,
        batch: UploadBatch,
        currentPosition: Int,
        total: Int,
        progress: Int
    ) {
        let now = Date().timeIntervalSince1970
        let shouldUpdate = now - lastProgressUpdateTime >= progressUpdateInterval
            || progress == 0
            || progress == 100
        guard shouldUpdate else { return }
        lastProgressUpdateTime = now

        updateImage(at: index) { item in
            var copy = item
            copy.uploadProgress = min(max(progress, 0), 100)
            return copy
        }
        uploadDialogState = ScenicApplicationUploadDialogState(
            title: "正在上传(\(currentPosition)/\(total))",
            progress: min(max(progress, 0), 100)
        )
        notifyStateChange()
    }

    private func updateImage(at index: Int, transform: (ScenicApplicationImageItem) -> ScenicApplicationImageItem) {
        guard index >= 0, index < selectedImages.count else { return }
        selectedImages[index] = transform(selectedImages[index])
    }

    private func adjustBatchAfterDeletion(deletedIndex: Int) {
        func adjust(_ batch: inout UploadBatch) {
            var removedBefore = 0
            var index = 0
            while index < batch.indices.count {
                let value = batch.indices[index]
                if value == deletedIndex {
                    if index < batch.position {
                        removedBefore += 1
                    }
                    batch.indices.remove(at: index)
                    if batch.totalCount > batch.completedCount {
                        batch.totalCount = max(batch.completedCount, batch.totalCount - 1)
                    }
                } else if value > deletedIndex {
                    batch.indices[index] = value - 1
                    index += 1
                } else {
                    index += 1
                }
            }
            batch.position = max(0, batch.position - removedBefore)
            if batch.position > batch.indices.count {
                batch.position = batch.indices.count
            }
        }

        if var batch = currentBatch {
            adjust(&batch)
            if batch.indices.isEmpty {
                currentBatch = nil
                uploadDialogState = nil
            } else {
                currentBatch = batch
            }
        }

        uploadBatchQueue = uploadBatchQueue.compactMap { batch in
            var copy = batch
            adjust(&copy)
            return copy.indices.isEmpty ? nil : copy
        }
    }

    private func notifyStateChange() {
        onStateChange?()
    }
}
