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

import Foundation

/// 摄影师任务列表实体，表示任务管理页中的一条任务记录。
struct PhotographerTaskItem: Decodable, Identifiable, Hashable {
    let id: Int
    let name: String
    let type: Int
    let orderNumber: String
    let taskStatus: Int
    let statusName: String
    let createdAt: String
    let updatedAt: String
    let photogRemark: String
    let editorName: String
    let operateTime: String
    let orderUserNickname: String
    let orderUserPhone: String
    let orderUserId: String
    let media: [TaskMediaItem]

    /// 任务列表字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case type
        case orderNumber = "order_number"
        case taskStatus = "task_status"
        case statusName = "status_name"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
        case photogRemark = "photog_remark"
        case editorName = "editor_name"
        case operateTime = "operate_time"
        case orderUserNickname = "order_user_nickname"
        case orderUserPhone = "order_user_phone"
        case orderUserId = "order_user_id"
        case media
    }

    /// 宽松解码任务列表字段，避免后端数字和字符串混用导致页面不可用。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        type = try container.decodeLossyInt(forKey: .type) ?? 0
        orderNumber = try container.decodeLossyString(forKey: .orderNumber)
        taskStatus = try container.decodeLossyInt(forKey: .taskStatus) ?? 0
        statusName = try container.decodeLossyString(forKey: .statusName)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        updatedAt = try container.decodeLossyString(forKey: .updatedAt)
        photogRemark = try container.decodeLossyString(forKey: .photogRemark)
        editorName = try container.decodeLossyString(forKey: .editorName)
        operateTime = try container.decodeLossyString(forKey: .operateTime)
        orderUserNickname = try container.decodeLossyString(forKey: .orderUserNickname)
        orderUserPhone = try container.decodeLossyString(forKey: .orderUserPhone)
        orderUserId = try container.decodeLossyString(forKey: .orderUserId)
        media = (try? container.decode([TaskMediaItem].self, forKey: .media)) ?? []
    }
}

/// 任务媒体实体，表示任务附件或任务结果中的图片、视频文件。
struct TaskMediaItem: Decodable, Identifiable, Hashable {
    let id: Int
    let photogTaskId: Int
    let userId: Int
    let fileName: String
    let fileType: Int
    let fileUrl: String
    let fileSize: Int64
    let coverUrl: String
    let duration: Int
    let remark: String
    let createdAt: String
    let updatedAt: String

    /// 判断当前媒体是否应按视频展示。
    var isVideo: Bool {
        fileType == 1 || Self.hasVideoExtension(fileName) || Self.hasVideoExtension(fileUrl)
    }

    /// 返回用于缩略图展示的 URL，优先使用封面图。
    var displayURL: String {
        coverUrl.isEmpty ? fileUrl : coverUrl
    }

    /// 根据文件扩展名判断是否是常见视频格式。
    private static func hasVideoExtension(_ value: String) -> Bool {
        ["mp4", "mov", "m4v", "avi"].contains(URL(string: value)?.pathExtension.lowercased() ?? URL(fileURLWithPath: value).pathExtension.lowercased())
    }

    /// 任务媒体字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case id
        case photogTaskId = "photog_task_id"
        case userId = "user_id"
        case fileName = "file_name"
        case fileType = "file_type"
        case fileUrl = "file_url"
        case fileSize = "file_size"
        case coverUrl = "cover_url"
        case duration
        case remark
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }

    /// 宽松解码任务媒体字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        photogTaskId = try container.decodeLossyInt(forKey: .photogTaskId) ?? 0
        userId = try container.decodeLossyInt(forKey: .userId) ?? 0
        fileName = try container.decodeLossyString(forKey: .fileName)
        fileType = try container.decodeLossyInt(forKey: .fileType) ?? 0
        fileUrl = try container.decodeLossyString(forKey: .fileUrl)
        fileSize = Int64(try container.decodeLossyInt(forKey: .fileSize) ?? 0)
        coverUrl = try container.decodeLossyString(forKey: .coverUrl)
        duration = try container.decodeLossyInt(forKey: .duration) ?? 0
        remark = try container.decodeLossyString(forKey: .remark)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        updatedAt = try container.decodeLossyString(forKey: .updatedAt)
    }
}

/// 任务详情实体，表示任务详情页展示的完整任务信息。
struct TaskDetailResponse: Decodable, Identifiable, Hashable {
    let id: Int
    let name: String
    let createdAt: String
    let taskStatus: Int
    let taskStatusLabel: String
    let acceptedAt: String
    let editorName: String
    let editorPhone: String
    let urgentHour: Int
    let photogRemark: String
    let order: TaskDetailOrder?
    let taskResult: [TaskMediaItem]

    /// 任务详情字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case createdAt = "created_at"
        case taskStatus = "task_status"
        case taskStatusLabel = "task_status_label"
        case acceptedAt = "accepted_at"
        case editorName = "editor_name"
        case editorPhone = "editor_phone"
        case urgentHour = "urgent_hour"
        case photogRemark = "photog_remark"
        case order
        case taskResult = "task_result"
    }

    /// 宽松解码任务详情字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        taskStatus = try container.decodeLossyInt(forKey: .taskStatus) ?? 0
        taskStatusLabel = try container.decodeLossyString(forKey: .taskStatusLabel)
        acceptedAt = try container.decodeLossyString(forKey: .acceptedAt)
        editorName = try container.decodeLossyString(forKey: .editorName)
        editorPhone = try container.decodeLossyString(forKey: .editorPhone)
        urgentHour = try container.decodeLossyInt(forKey: .urgentHour) ?? 0
        photogRemark = try container.decodeLossyString(forKey: .photogRemark)
        order = try? container.decode(TaskDetailOrder.self, forKey: .order)
        taskResult = (try? container.decode([TaskMediaItem].self, forKey: .taskResult)) ?? []
    }
}

/// 任务关联订单实体，表示任务详情中的订单摘要。
struct TaskDetailOrder: Decodable, Hashable {
    let projectName: String
    let orderNumber: String
    let payTime: String
    let userPhone: String

    /// 任务关联订单字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case projectName = "project_name"
        case orderNumber = "order_number"
        case payTime = "pay_time"
        case userPhone = "user_phone"
    }

    /// 宽松解码订单摘要字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        projectName = try container.decodeLossyString(forKey: .projectName)
        orderNumber = try container.decodeLossyString(forKey: .orderNumber)
        payTime = try container.decodeLossyString(forKey: .payTime)
        userPhone = try container.decodeLossyString(forKey: .userPhone)
    }
}

/// 发布任务请求实体，描述提交给后端的新任务内容。
struct AddTaskRequest: Encodable, Equatable {
    let scenicId: String
    let name: String
    let orderNumber: String?
    let remark: String
    let urgentHour: Int
    let cloudFile: [CloudFileItem]
    let uploadFile: [UploadFileItem]

    /// 发布任务请求字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case scenicId = "scenic_id"
        case name
        case orderNumber = "order_number"
        case remark = "photog_remark"
        case urgentHour = "urgent_hour"
        case cloudFile = "cloud_file"
        case uploadFile = "upload_file"
    }
}

/// 任务云盘附件请求实体，表示发布任务时选择的云盘文件。
struct CloudFileItem: Encodable, Equatable {
    let fileId: Int
    let remark: String

    /// 云盘附件请求字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case fileId = "file_id"
        case remark
    }
}

/// 任务上传附件请求实体，表示发布任务时上传到 OSS 的本地文件。
struct UploadFileItem: Encodable, Equatable {
    let fileUrl: String
    let fileName: String
    let remark: String

    /// 上传附件请求字段映射，兼容后端下划线命名。
    enum CodingKeys: String, CodingKey {
        case fileUrl = "file_url"
        case fileName = "file_name"
        case remark
    }
}

private extension KeyedDecodingContainer {
    /// 将 String、数字和 Bool 宽松解码为字符串。
    func decodeLossyString(forKey key: Key) throws -> String {
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return String(value)
        }
        if let value = try? decodeIfPresent(Double.self, forKey: key) {
            return String(value)
        }
        if let value = try? decodeIfPresent(Bool.self, forKey: key) {
            return value ? "true" : "false"
        }
        return ""
    }

    /// 将 Int、Double 或数字字符串宽松解码为整数。
    func decodeLossyInt(forKey key: Key) throws -> Int? {
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
            if let intValue = Int(text) {
                return intValue
            }
            if let doubleValue = Double(text) {
                return Int(doubleValue)
            }
        }
        if let value = try? decodeIfPresent(Double.self, forKey: key) {
            return Int(value)
        }
        return nil
    }
}
