//
//  TaskCreateViewModel.swift
//  suixinkan
//
//  Created by Codex on 2026/6/23.
//

import Foundation
import Combine

/// 任务云盘选择项实体，表示发布任务表单中的云盘附件。
struct TaskCloudSelectionItem: Identifiable, Hashable {
    let id: Int
    let fileName: String
    let fileType: Int
    var remark: String
}

/// 任务本地上传项实体，表示发布任务表单中的本地图片或视频。
struct TaskLocalUploadItem: Identifiable, Hashable {
    let id: UUID
    let data: Data
    let fileName: String
    let fileType: Int
    var remark: String
    var uploadedURL: String?
    var progress: Int
    var errorMessage: String?

    /// 判断当前文件是否正在上传。
    var isUploading: Bool {
        progress > 0 && progress < 100 && uploadedURL == nil && errorMessage == nil
    }

    /// 返回上传状态展示文案。
    var statusText: String {
        if let errorMessage { return errorMessage }
        if uploadedURL != nil { return "已上传" }
        if progress > 0 { return "上传中 \(progress)%" }
        return "待上传"
    }
}

/// 发布任务 ViewModel，负责表单、附件上传和提交任务。
@MainActor
final class TaskCreateViewModel: ObservableObject {
    @Published var taskName = ""
    @Published var remark = ""
    @Published var urgentHourText = "0"
    @Published var selectedOrder: AvailableOrderResponse?
    @Published var availableOrders: [AvailableOrderResponse] = []
    @Published var selectedCloudFiles: [TaskCloudSelectionItem] = []
    @Published var selectedLocalFiles: [TaskLocalUploadItem] = []
    private(set) var isLoadingOrders = false
    @Published var isSubmitting = false
    @Published var errorMessage: String?
    @Published var didSubmitSuccessfully = false

    /// 判断当前表单是否满足提交按钮启用条件。
    var canSubmit: Bool {
        validateForm(scenicId: 1, shouldSetError: false)
            && !isSubmitting
            && !selectedLocalFiles.contains(where: \.isUploading)
    }

    /// 加载可关联订单列表。
    func loadAvailableOrders(api: any TaskServing, scenicId: Int?) async {
        guard let scenicId else {
            availableOrders = []
            return
        }
        isLoadingOrders = true
        defer { isLoadingOrders = false }

        do {
            availableOrders = try await api.availableOrderList(scenicId: scenicId)
        } catch {
            availableOrders = []
            errorMessage = error.localizedDescription
        }
    }

    /// 选择或清除关联订单。
    func selectOrder(_ order: AvailableOrderResponse?) {
        selectedOrder = order
    }

    /// 添加云盘附件，按文件 ID 去重。
    func mergeCloudFiles(_ files: [TaskCloudSelectionItem]) {
        var next = selectedCloudFiles
        for file in files where !next.contains(where: { $0.id == file.id }) {
            next.append(file)
        }
        selectedCloudFiles = next
    }

    /// 移除指定云盘附件。
    func removeCloudFile(id: Int) {
        selectedCloudFiles.removeAll { $0.id == id }
    }

    /// 添加本地附件数据。
    func addLocalFile(data: Data, fileName: String) {
        let fileType = Self.fileType(for: fileName)
        selectedLocalFiles.append(
            TaskLocalUploadItem(
                id: UUID(),
                data: data,
                fileName: fileName,
                fileType: fileType,
                remark: "",
                uploadedURL: nil,
                progress: 0,
                errorMessage: nil
            )
        )
    }

    /// 移除指定本地附件。
    func removeLocalFile(id: UUID) {
        selectedLocalFiles.removeAll { $0.id == id }
    }

    /// 提交任务；本地附件会先上传 OSS，成功后再提交最终 URL。
    @discardableResult
    func submit(api: any TaskServing, uploadService: any OSSUploadServing, scenicId: Int?) async throws -> Bool {
        guard !isSubmitting else { return false }
        guard validateForm(scenicId: scenicId, shouldSetError: true) else { return false }
        guard let scenicId, let urgentHour = Int(urgentHourText.trimmingCharacters(in: .whitespacesAndNewlines)) else { return false }

        isSubmitting = true
        errorMessage = nil
        didSubmitSuccessfully = false
        defer { isSubmitting = false }

        do {
            let uploadedFiles = try await uploadLocalFiles(uploadService: uploadService, scenicId: scenicId)
            let request = AddTaskRequest(
                scenicId: "\(scenicId)",
                name: taskName.trimmingCharacters(in: .whitespacesAndNewlines),
                orderNumber: selectedOrder?.orderNumber,
                remark: remark.trimmingCharacters(in: .whitespacesAndNewlines),
                urgentHour: urgentHour,
                cloudFile: selectedCloudFiles.map { CloudFileItem(fileId: $0.id, remark: $0.remark) },
                uploadFile: uploadedFiles
            )
            try await api.addTask(request)
            didSubmitSuccessfully = true
            resetForm()
            return true
        } catch {
            errorMessage = error.localizedDescription
            throw error
        }
    }

    /// 清空发布任务表单。
    func resetForm() {
        taskName = ""
        remark = ""
        urgentHourText = "0"
        selectedOrder = nil
        selectedCloudFiles = []
        selectedLocalFiles = []
    }

    /// 校验表单并按需写入错误提示。
    private func validateForm(scenicId: Int?, shouldSetError: Bool) -> Bool {
        func fail(_ message: String) -> Bool {
            if shouldSetError { errorMessage = message }
            return false
        }

        guard scenicId != nil else {
            return fail("请先选择景区")
        }
        guard !taskName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            return fail("请输入任务名称")
        }
        guard !remark.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
            return fail("请输入任务备注")
        }
        guard let urgentHour = Int(urgentHourText.trimmingCharacters(in: .whitespacesAndNewlines)), urgentHour >= 0 else {
            return fail("请输入正确的紧急小时")
        }
        guard !selectedLocalFiles.contains(where: \.isUploading) else {
            return fail("文件上传中，请稍后提交")
        }
        return true
    }

    /// 上传所有尚未上传的本地附件并返回提交请求需要的文件列表。
    private func uploadLocalFiles(uploadService: any OSSUploadServing, scenicId: Int) async throws -> [UploadFileItem] {
        var uploadedFiles: [UploadFileItem] = []
        for index in selectedLocalFiles.indices {
            if let uploadedURL = selectedLocalFiles[index].uploadedURL {
                uploadedFiles.append(
                    UploadFileItem(
                        fileUrl: uploadedURL,
                        fileName: selectedLocalFiles[index].fileName,
                        remark: selectedLocalFiles[index].remark
                    )
                )
                continue
            }

            selectedLocalFiles[index].errorMessage = nil
            let fileID = selectedLocalFiles[index].id
            do {
                let url = try await uploadService.uploadTaskFile(
                    data: selectedLocalFiles[index].data,
                    fileName: selectedLocalFiles[index].fileName,
                    fileType: selectedLocalFiles[index].fileType,
                    scenicId: scenicId,
                    onProgress: { [weak self] progress in
                        Task { @MainActor in
                            self?.updateLocalFileProgress(id: fileID, progress: progress)
                        }
                    }
                )
                selectedLocalFiles[index].uploadedURL = url
                selectedLocalFiles[index].progress = 100
                uploadedFiles.append(
                    UploadFileItem(
                        fileUrl: url,
                        fileName: selectedLocalFiles[index].fileName,
                        remark: selectedLocalFiles[index].remark
                    )
                )
            } catch {
                selectedLocalFiles[index].progress = 0
                selectedLocalFiles[index].errorMessage = error.localizedDescription
                throw error
            }
        }
        return uploadedFiles
    }

    /// 更新指定本地附件的上传进度。
    private func updateLocalFileProgress(id: UUID, progress: Int) {
        guard let index = selectedLocalFiles.firstIndex(where: { $0.id == id }) else { return }
        selectedLocalFiles[index].progress = progress
    }

    /// 根据文件名推断后端需要的文件类型。
    private static func fileType(for fileName: String) -> Int {
        let ext = URL(fileURLWithPath: fileName).pathExtension.lowercased()
        return ["mp4", "mov", "m4v", "avi"].contains(ext) ? 1 : 2
    }
}
