//
//  OrderModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/22.
//

import Foundation

/// 订单 Tab 内的子入口，表示订单管理或核销订单。
enum OrdersEntry: Hashable {
    case storeOrders
    case verificationOrders
}

/// 订单状态筛选实体，表示订单管理列表顶部的状态过滤项。
struct OrderStatusFilter: Identifiable, Hashable {
    let id: Int
    let title: String
}

/// 订单筛选常量，集中维护旧工程同步过来的订单状态。
enum OrderFilters {
    static let statusFilters: [OrderStatusFilter] = [
        .init(id: 0, title: "全部"),
        .init(id: 18, title: "已付定金"),
        .init(id: 30, title: "已完成"),
        .init(id: 50, title: "已退款"),
        .init(id: -1, title: "需要精修"),
        .init(id: -2, title: "无需精修")
    ]
}

/// 订单管理列表实体，承载订单卡片展示和筛选判断所需字段。
struct OrderEntity: Decodable, Identifiable, Equatable, Hashable {
    var id: String { orderNumber }

    let photogUid: Int
    let orderNumber: String
    let remark: String
    let scenicAreaId: Int
    let payTime: String
    let completeTime: String
    let orderStatus: Int
    let orderStatusName: String
    let storeId: Int?
    let userId: Int
    let projectId: Int
    let phone: String
    let orderAmount: String
    let actualPayAmount: String
    let actualRefundAmount: String
    let refundAmount: String
    let payTypeName: String
    let payType: Int
    let projectName: String
    let orderType: Int
    let orderTypeLabel: String
    let depositPayTime: String
    let createdAt: String
    let photoTravel: OrderPhotoTravel?
    let isNeedEdit: Bool
    let isRefined: Int

    enum CodingKeys: String, CodingKey {
        case photogUid = "photog_uid"
        case orderNumber = "order_number"
        case remark
        case scenicAreaId = "scenic_area_id"
        case payTime = "pay_time"
        case completeTime = "complete_time"
        case orderStatus = "order_status"
        case orderStatusName = "order_status_name"
        case storeId = "store_id"
        case userId = "user_id"
        case projectId = "project_id"
        case phone
        case orderAmount = "order_amount"
        case actualPayAmount = "actual_pay_amount"
        case actualRefundAmount = "actual_refund_amount"
        case refundAmount = "refund_amount"
        case payTypeName = "pay_type_name"
        case payType = "pay_type"
        case projectName = "project_name"
        case orderType = "order_type"
        case orderTypeLabel = "order_type_label"
        case depositPayTime = "deposit_pay_time"
        case createdAt = "created_at"
        case photoTravel = "photo_travel"
        case isNeedEdit
        case isRefined = "is_refined"
    }

    /// 宽松解码订单字段，兼容后端数字和字符串混用。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        photogUid = try container.decodeLossyInt(forKey: .photogUid) ?? 0
        orderNumber = try container.decodeLossyString(forKey: .orderNumber)
        remark = try container.decodeLossyString(forKey: .remark)
        scenicAreaId = try container.decodeLossyInt(forKey: .scenicAreaId) ?? 0
        payTime = try container.decodeLossyString(forKey: .payTime)
        completeTime = try container.decodeLossyString(forKey: .completeTime)
        orderStatus = try container.decodeLossyInt(forKey: .orderStatus) ?? 0
        orderStatusName = try container.decodeLossyString(forKey: .orderStatusName)
        storeId = try container.decodeLossyInt(forKey: .storeId)
        userId = try container.decodeLossyInt(forKey: .userId) ?? 0
        projectId = try container.decodeLossyInt(forKey: .projectId) ?? 0
        phone = try container.decodeLossyString(forKey: .phone)
        orderAmount = try container.decodeLossyString(forKey: .orderAmount)
        actualPayAmount = try container.decodeLossyString(forKey: .actualPayAmount)
        actualRefundAmount = try container.decodeLossyString(forKey: .actualRefundAmount)
        refundAmount = try container.decodeLossyString(forKey: .refundAmount)
        payTypeName = try container.decodeLossyString(forKey: .payTypeName)
        payType = try container.decodeLossyInt(forKey: .payType) ?? 0
        projectName = try container.decodeLossyString(forKey: .projectName)
        orderType = try container.decodeLossyInt(forKey: .orderType) ?? 0
        orderTypeLabel = try container.decodeLossyString(forKey: .orderTypeLabel)
        depositPayTime = try container.decodeLossyString(forKey: .depositPayTime)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        photoTravel = try container.decodeIfPresent(OrderPhotoTravel.self, forKey: .photoTravel)
        isNeedEdit = try container.decodeLossyBool(forKey: .isNeedEdit) ?? true
        isRefined = try container.decodeLossyInt(forKey: .isRefined) ?? 0
    }
}

/// 旅拍订单附加信息实体，表示修图、视频和核销相关数量。
struct OrderPhotoTravel: Decodable, Equatable, Hashable {
    let orderPhotoNum: Int
    let orderVideoNum: Int
    let checkInTime: String
    let retouchGiftPhotoNum: Int
    let retouchGiftVideoNum: Int

    enum CodingKeys: String, CodingKey {
        case orderPhotoNum = "order_photo_num"
        case orderVideoNum = "order_video_num"
        case checkInTime = "check_in_time"
        case retouchGiftPhotoNum = "retouch_gift_photo_num"
        case retouchGiftVideoNum = "retouch_gift_video_num"
    }

    /// 宽松解码旅拍附加信息。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        orderPhotoNum = try container.decodeLossyInt(forKey: .orderPhotoNum) ?? 0
        orderVideoNum = try container.decodeLossyInt(forKey: .orderVideoNum) ?? 0
        checkInTime = try container.decodeLossyString(forKey: .checkInTime)
        retouchGiftPhotoNum = try container.decodeLossyInt(forKey: .retouchGiftPhotoNum) ?? 0
        retouchGiftVideoNum = try container.decodeLossyInt(forKey: .retouchGiftVideoNum) ?? 0
    }
}

/// 核销订单列表实体，表示可核销订单卡片的展示和操作状态。
struct WriteOffOrderItem: Decodable, Identifiable, Equatable, Hashable {
    var id: String { orderNumber }

    let orderNumber: String
    let orderVerificationStatus: String
    let payTime: String
    let projectName: String
    let userPhone: String
    let orderAmount: String
    let orderStatusName: String
    let orderVerificationTime: String

    enum CodingKeys: String, CodingKey {
        case orderNumber = "order_number"
        case orderVerificationStatus = "order_verification_status"
        case payTime = "pay_time"
        case projectName = "project_name"
        case userPhone = "user_phone"
        case orderAmount = "order_amount"
        case orderStatusName = "order_status_name"
        case orderVerificationTime = "order_verification_time"
    }

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

/// 核销订单请求实体，提交手动输入的订单号。
struct WriteOffRequest: Encodable {
    let orderNumber: String

    enum CodingKeys: String, CodingKey {
        case orderNumber = "order_number"
    }
}

/// 订单详情路由实体，表示订单模块内部可 push 的二级页面。
enum OrdersRoute: Hashable {
    case storeDetail(OrderEntity)
    case writeOffDetail(WriteOffOrderItem)
}

/// 订单详情接口响应实体，承载门店订单详情页的完整展示数据。
struct StoreOrderDetailResponse: Decodable, Equatable, Hashable {
    let orderNumber: String
    let orderType: Int
    let orderTypeLabel: String
    let createdAt: String
    let orderStatus: Int
    let orderStatusName: String
    let actualPayAmount: String
    let actualRefundAmount: String
    let payTypeName: String
    let payTime: String
    let completeTime: String
    let userId: Int
    let phone: String
    let projectId: Int
    let projectName: String
    let remark: String
    let multiTravel: StoreOrderMultiTravelInfo?

    enum CodingKeys: String, CodingKey {
        case orderNumber = "order_number"
        case orderType = "order_type"
        case orderTypeLabel = "order_type_label"
        case createdAt = "created_at"
        case orderStatus = "order_status"
        case orderStatusName = "order_status_name"
        case actualPayAmount = "actual_pay_amount"
        case actualRefundAmount = "actual_refund_amount"
        case payTypeName = "pay_type_name"
        case payTime = "pay_time"
        case completeTime = "complete_time"
        case userId = "user_id"
        case phone
        case projectId = "project_id"
        case projectName = "project_name"
        case remark
        case multiTravel = "multi_travel"
    }

    /// 宽松解码订单详情字段，兼容后端数字和字符串混用。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        orderNumber = try container.decodeLossyString(forKey: .orderNumber)
        orderType = try container.decodeLossyInt(forKey: .orderType) ?? 0
        orderTypeLabel = try container.decodeLossyString(forKey: .orderTypeLabel)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        orderStatus = try container.decodeLossyInt(forKey: .orderStatus) ?? 0
        orderStatusName = try container.decodeLossyString(forKey: .orderStatusName)
        actualPayAmount = try container.decodeLossyString(forKey: .actualPayAmount)
        actualRefundAmount = try container.decodeLossyString(forKey: .actualRefundAmount)
        payTypeName = try container.decodeLossyString(forKey: .payTypeName)
        payTime = try container.decodeLossyString(forKey: .payTime)
        completeTime = try container.decodeLossyString(forKey: .completeTime)
        userId = try container.decodeLossyInt(forKey: .userId) ?? 0
        phone = try container.decodeLossyString(forKey: .phone)
        projectId = try container.decodeLossyInt(forKey: .projectId) ?? 0
        projectName = try container.decodeLossyString(forKey: .projectName)
        remark = try container.decodeLossyString(forKey: .remark)
        multiTravel = try container.decodeIfPresent(StoreOrderMultiTravelInfo.self, forKey: .multiTravel)
    }
}

/// 多景点旅拍详情实体，表示项目配置和各拍摄点状态。
struct StoreOrderMultiTravelInfo: Decodable, Equatable, Hashable {
    let projectInfo: StoreOrderProjectInfo?
    let shootingList: [StoreOrderShootingListItem]

    enum CodingKeys: String, CodingKey {
        case projectInfo = "project_info"
        case shootingList = "shooting_list"
    }

    /// 宽松解码多景点旅拍信息。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        projectInfo = try container.decodeIfPresent(StoreOrderProjectInfo.self, forKey: .projectInfo)
        shootingList = try container.decodeIfPresent([StoreOrderShootingListItem].self, forKey: .shootingList) ?? []
    }
}

/// 订单项目配置实体，表示打卡点数量和单点素材数量。
struct StoreOrderProjectInfo: Decodable, Equatable, Hashable {
    let settleSpotNum: Int
    let singleSpotMaterialNum: Int
    let singleSpotPhotoNum: Int
    let singleSpotVideoNum: Int

    enum CodingKeys: String, CodingKey {
        case settleSpotNum = "settle_spot_num"
        case singleSpotMaterialNum = "single_spot_material_num"
        case singleSpotPhotoNum = "single_spot_photo_num"
        case singleSpotVideoNum = "single_spot_video_num"
    }

    /// 宽松解码项目配置字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        settleSpotNum = try container.decodeLossyInt(forKey: .settleSpotNum) ?? 0
        singleSpotMaterialNum = try container.decodeLossyInt(forKey: .singleSpotMaterialNum) ?? 0
        singleSpotPhotoNum = try container.decodeLossyInt(forKey: .singleSpotPhotoNum) ?? 0
        singleSpotVideoNum = try container.decodeLossyInt(forKey: .singleSpotVideoNum) ?? 0
    }
}

/// 拍摄点状态实体，表示订单在某个打卡点的拍摄人员和完成情况。
struct StoreOrderShootingListItem: Decodable, Identifiable, Equatable, Hashable {
    var id: String { "\(scenicSpotId)-\(photogUid)" }

    let scenicSpotId: Int
    let photogUid: Int
    let scenicSpotName: String
    let staffName: String
    let photogName: String
    let status: Int
    let startAvg: Double
    let start: Double

    enum CodingKeys: String, CodingKey {
        case scenicSpotId = "scenic_spot_id"
        case photogUid = "photog_uid"
        case scenicSpotName = "scenic_spot_name"
        case staffName = "staff_name"
        case photogName = "photog_name"
        case status
        case startAvg = "start_avg"
        case start
    }

    /// 宽松解码拍摄点状态字段。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        scenicSpotId = try container.decodeLossyInt(forKey: .scenicSpotId) ?? 0
        photogUid = try container.decodeLossyInt(forKey: .photogUid) ?? 0
        scenicSpotName = try container.decodeLossyString(forKey: .scenicSpotName)
        staffName = try container.decodeLossyString(forKey: .staffName)
        photogName = try container.decodeLossyString(forKey: .photogName)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        startAvg = try container.decodeLossyDouble(forKey: .startAvg) ?? 0
        start = try container.decodeLossyDouble(forKey: .start) ?? 0
    }
}

/// 订单详情展示适配实体，统一列表摘要和详情接口响应的读取方式。
struct StoreOrderDetailDisplay: Equatable {
    let orderNumber: String
    let orderTypeLabel: String
    let createdAt: String
    let orderStatusName: String
    let actualPayAmount: String
    let actualRefundAmount: String
    let payTypeName: String
    let payTime: String
    let completeTime: String
    let userId: Int
    let phone: String
    let projectId: Int
    let projectName: String
    let remark: String

    /// 使用订单列表项构建兜底展示数据。
    init(item: OrderEntity) {
        orderNumber = item.orderNumber
        orderTypeLabel = item.orderTypeLabel
        createdAt = item.createdAt
        orderStatusName = item.orderStatusName
        actualPayAmount = item.actualPayAmount
        actualRefundAmount = item.actualRefundAmount.isEmpty ? item.refundAmount : item.actualRefundAmount
        payTypeName = item.payTypeName
        payTime = item.payTime
        completeTime = item.completeTime
        userId = item.userId
        phone = item.phone
        projectId = item.projectId
        projectName = item.projectName
        remark = item.remark
    }

    /// 使用详情接口响应构建完整展示数据。
    init(detail: StoreOrderDetailResponse) {
        orderNumber = detail.orderNumber
        orderTypeLabel = detail.orderTypeLabel
        createdAt = detail.createdAt
        orderStatusName = detail.orderStatusName
        actualPayAmount = detail.actualPayAmount
        actualRefundAmount = detail.actualRefundAmount
        payTypeName = detail.payTypeName
        payTime = detail.payTime
        completeTime = detail.completeTime
        userId = detail.userId
        phone = detail.phone
        projectId = detail.projectId
        projectName = detail.projectName
        remark = detail.remark
    }
}

/// 订单号解析器，负责从扫码内容或手动输入中提取可核销订单号。
enum OrderNumberParser {
    /// 从原始文本中解析订单号，兼容 URL、键值格式和纯订单号。
    static func parse(_ raw: String) -> String? {
        let text = raw.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !text.isEmpty else { return nil }

        if let url = URL(string: text),
           let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
           let queryItems = components.queryItems,
           let value = queryItems.first(where: { $0.name.lowercased() == "order_number" })?.value,
           !value.isEmpty {
            return value
        }

        let patterns = [
            "order[_-]?number[=:]([A-Za-z0-9_-]+)",
            "([A-Za-z0-9_-]{8,})"
        ]
        for pattern in patterns {
            if let match = firstCapture(in: text, pattern: pattern) {
                return match
            }
        }

        if !text.contains(" "), text.count >= 6 {
            return text
        }
        return nil
    }

    /// 返回第一个正则捕获组。
    private static func firstCapture(in text: String, pattern: String) -> String? {
        guard let regex = try? NSRegularExpression(pattern: pattern, options: [.caseInsensitive]) else {
            return nil
        }
        let range = NSRange(text.startIndex..<text.endIndex, in: text)
        guard let match = regex.firstMatch(in: text, options: [], range: range),
              match.numberOfRanges > 1,
              let captureRange = Range(match.range(at: 1), in: text) else {
            return nil
        }
        return String(text[captureRange])
    }
}

private extension KeyedDecodingContainer {
    /// 将任意常见 JSON 值宽松解码成字符串。
    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 ? "1" : "0"
        }
        return ""
    }

    /// 将 Int、Double、Bool 或数字字符串宽松解码成整数。
    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)
        }
        if let value = try? decodeIfPresent(Bool.self, forKey: key) {
            return value ? 1 : 0
        }
        return nil
    }

    /// 将 Bool、Int 或字符串宽松解码成布尔值。
    func decodeLossyBool(forKey key: Key) throws -> Bool? {
        if let value = try? decodeIfPresent(Bool.self, forKey: key) {
            return value
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return value != 0
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            switch value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() {
            case "1", "true", "yes":
                return true
            case "0", "false", "no":
                return false
            default:
                return nil
            }
        }
        return nil
    }

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