//
//  OrderModels.swift
//  suixinkan
//

import Foundation

// MARK: - List Response

/// 订单列表分页响应，字段为 `list`（非 `data`）。
struct OrderListResponse<T: Decodable>: Decodable {
    let total: Int
    let list: [T]

    init(total: Int = 0, list: [T] = []) {
        self.total = total
        self.list = list
    }
}

// MARK: - Photographer Order

struct OrderEntity: Decodable, Equatable {
    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 userId: Int
    let projectId: Int
    let phone: String
    let orderAmount: String
    let actualPayAmount: String
    let actualRefundAmount: String
    let payTypeName: String
    let payType: Int
    let projectName: String
    let orderType: Int
    let orderTypeLabel: String
    let depositPayTime: String
    let createdAt: String
    let photoTravel: PhotoTravelEntity?
    let refundAmount: String
    let multiTravel: MultiTravelEntity?
    let isNeedEdit: Bool
    let isRefined: Int
    let canBindReferral: Int
    let showOrderSource: Int
    let referralOrderNo: String
    let referralSalerName: String
    let referralSalerPhotogRemarkName: String
    let referralOrder: BoundReferralOrderEntity?

    var displayReferralSalerName: String {
        OrderDisplayName.saleUser(remark: referralSalerPhotogRemarkName, primary: referralSalerName)
    }

    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 userId = "user_id"
        case projectId = "project_id"
        case phone
        case orderAmount = "order_amount"
        case actualPayAmount = "actual_pay_amount"
        case actualRefundAmount = "actual_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 refundAmount = "refund_amount"
        case multiTravel = "multi_travel"
        case isNeedEdit
        case isRefined = "is_refined"
        case canBindReferral = "can_bind_referral"
        case showOrderSource = "show_order_source"
        case referralOrderNo = "referral_order_no"
        case referralSalerName = "referral_saler_name"
        case referralSalerPhotogRemarkName = "photog_remark_name"
        case referralOrder = "referral_order"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        photogUid = try container.decodeIfPresent(Int.self, forKey: .photogUid) ?? 0
        orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber) ?? ""
        remark = try container.decodeIfPresent(String.self, forKey: .remark)
        scenicAreaId = try container.decodeIfPresent(Int.self, forKey: .scenicAreaId) ?? 0
        payTime = try container.decodeIfPresent(String.self, forKey: .payTime) ?? ""
        completeTime = try container.decodeIfPresent(String.self, forKey: .completeTime) ?? ""
        orderStatus = try container.decodeIfPresent(Int.self, forKey: .orderStatus) ?? 0
        orderStatusName = try container.decodeIfPresent(String.self, forKey: .orderStatusName) ?? ""
        userId = try container.decodeIfPresent(Int.self, forKey: .userId) ?? 0
        projectId = try container.decodeIfPresent(Int.self, forKey: .projectId) ?? 0
        phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? ""
        orderAmount = try Self.decodeFlexibleString(container: container, key: .orderAmount)
        actualPayAmount = try Self.decodeFlexibleString(container: container, key: .actualPayAmount)
        actualRefundAmount = try Self.decodeFlexibleString(container: container, key: .actualRefundAmount, defaultValue: "0")
        payTypeName = try container.decodeIfPresent(String.self, forKey: .payTypeName) ?? ""
        payType = try container.decodeIfPresent(Int.self, forKey: .payType) ?? 0
        projectName = try container.decodeIfPresent(String.self, forKey: .projectName) ?? ""
        orderType = try container.decodeIfPresent(Int.self, forKey: .orderType) ?? 0
        orderTypeLabel = try container.decodeIfPresent(String.self, forKey: .orderTypeLabel) ?? ""
        depositPayTime = try container.decodeIfPresent(String.self, forKey: .depositPayTime) ?? ""
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
        photoTravel = try Self.decodePhotoTravel(from: container)
        refundAmount = try Self.decodeFlexibleString(container: container, key: .refundAmount, defaultValue: "0")
        multiTravel = try Self.decodeMultiTravel(from: container)
        isNeedEdit = try container.decodeIfPresent(Bool.self, forKey: .isNeedEdit) ?? true
        isRefined = try container.decodeIfPresent(Int.self, forKey: .isRefined) ?? 0
        canBindReferral = try container.decodeIfPresent(Int.self, forKey: .canBindReferral) ?? 0
        showOrderSource = try container.decodeIfPresent(Int.self, forKey: .showOrderSource) ?? 0
        referralOrderNo = try container.decodeIfPresent(String.self, forKey: .referralOrderNo) ?? ""
        referralSalerName = try container.decodeIfPresent(String.self, forKey: .referralSalerName) ?? ""
        referralSalerPhotogRemarkName = try container.decodeIfPresent(String.self, forKey: .referralSalerPhotogRemarkName) ?? ""
        referralOrder = try container.decodeIfPresent(BoundReferralOrderEntity.self, forKey: .referralOrder)
    }

    private static func decodeFlexibleString(
        container: KeyedDecodingContainer<CodingKeys>,
        key: CodingKeys,
        defaultValue: String = ""
    ) throws -> String {
        if let string = try? container.decode(String.self, forKey: key) {
            return string
        }
        if let intValue = try? container.decode(Int.self, forKey: key) {
            return String(intValue)
        }
        if let doubleValue = try? container.decode(Double.self, forKey: key) {
            return String(doubleValue)
        }
        return defaultValue
    }

    private static func decodePhotoTravel(
        from container: KeyedDecodingContainer<CodingKeys>
    ) throws -> PhotoTravelEntity? {
        guard container.contains(.photoTravel), try !container.decodeNil(forKey: .photoTravel) else {
            return nil
        }
        let travel = try container.decode(PhotoTravelEntity.self, forKey: .photoTravel)
        return travel.isEmpty ? nil : travel
    }

    private static func decodeMultiTravel(
        from container: KeyedDecodingContainer<CodingKeys>
    ) throws -> MultiTravelEntity? {
        guard container.contains(.multiTravel), try !container.decodeNil(forKey: .multiTravel) else {
            return nil
        }
        let travel = try container.decode(MultiTravelEntity.self, forKey: .multiTravel)
        return travel.isEmpty ? nil : travel
    }
}

struct BoundReferralOrderEntity: Decodable, Equatable {
    let id: Int
    let orderNo: String
    let saleUserId: Int
    let salerName: String
    let photogRemarkName: String
    let salerPhone: String
    let displayText: String
    let userMobile: String
    let remark: String
    let bindOrderNumber: String
    let sourceType: Int
    let sourceTypeLabel: String
    let status: Int
    let statusLabel: String
    let createdAt: String

    var displaySalerName: String {
        OrderDisplayName.saleUser(remark: photogRemarkName, primary: salerName)
    }

    enum CodingKeys: String, CodingKey {
        case id
        case orderNo = "order_no"
        case saleUserId = "sale_user_id"
        case salerName = "saler_name"
        case photogRemarkName = "photog_remark_name"
        case salerPhone = "saler_phone"
        case displayText = "display_text"
        case userMobile = "user_mobile"
        case remark
        case bindOrderNumber = "bind_order_number"
        case sourceType = "source_type"
        case sourceTypeLabel = "source_type_label"
        case status
        case statusLabel = "status_label"
        case createdAt = "created_at"
        case saleUser = "sale_user"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0
        orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? ""
        saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0
        salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? ""
        salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? ""
        displayText = try container.decodeIfPresent(String.self, forKey: .displayText) ?? ""
        userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? ""
        remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? ""
        bindOrderNumber = try container.decodeIfPresent(String.self, forKey: .bindOrderNumber) ?? ""
        sourceType = try container.decodeIfPresent(Int.self, forKey: .sourceType) ?? 0
        sourceTypeLabel = try container.decodeIfPresent(String.self, forKey: .sourceTypeLabel) ?? ""
        status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
        statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""

        let saleUser = try container.decodeIfPresent(ReferralSaleUserEntity.self, forKey: .saleUser)
        photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName)
            ?? saleUser?.photogRemarkName
            ?? saleUser?.displayName
            ?? ""
    }
}

/// `referral_order.sale_user` 嵌套对象，接口字段与顶层可能重复。
struct ReferralSaleUserEntity: Decodable, Equatable {
    let photogRemarkName: String
    let displayName: String
    let salerName: String

    enum CodingKeys: String, CodingKey {
        case photogRemarkName = "photog_remark_name"
        case displayName = "display_name"
        case salerName = "saler_name"
        case name
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? ""
        let name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
        salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? name
        displayName = try container.decodeIfPresent(String.self, forKey: .displayName) ?? salerName
    }
}

struct PhotoTravelEntity: Decodable, Equatable {
    let orderPhotoNum: Int
    let orderVideoNum: Int
    let canCancelOrder: Bool
    let needCheckIn: Bool
    let checkInTime: String
    let canGiftRetouch: Bool
    let retouchGiftTime: String
    let retouchGiftPhotoNum: Int
    let retouchGiftVideoNum: Int

    enum CodingKeys: String, CodingKey {
        case orderPhotoNum = "order_photo_num"
        case orderVideoNum = "order_video_num"
        case canCancelOrder = "can_cancel_order"
        case needCheckIn = "need_check_in"
        case checkInTime = "check_in_time"
        case canGiftRetouch = "can_gift_retouch"
        case retouchGiftTime = "retouch_gift_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.decodeIfPresent(Int.self, forKey: .orderPhotoNum) ?? 0
        orderVideoNum = try container.decodeIfPresent(Int.self, forKey: .orderVideoNum) ?? 0
        canCancelOrder = try container.decodeIfPresent(Bool.self, forKey: .canCancelOrder) ?? false
        needCheckIn = try container.decodeIfPresent(Bool.self, forKey: .needCheckIn) ?? false
        checkInTime = try container.decodeIfPresent(String.self, forKey: .checkInTime) ?? ""
        canGiftRetouch = try container.decodeIfPresent(Bool.self, forKey: .canGiftRetouch) ?? false
        retouchGiftTime = try container.decodeIfPresent(String.self, forKey: .retouchGiftTime) ?? ""
        retouchGiftPhotoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftPhotoNum) ?? 0
        retouchGiftVideoNum = try container.decodeIfPresent(Int.self, forKey: .retouchGiftVideoNum) ?? 0
    }

    var isEmpty: Bool {
        orderPhotoNum == 0
            && orderVideoNum == 0
            && !canCancelOrder
            && !needCheckIn
            && checkInTime.isEmpty
            && !canGiftRetouch
            && retouchGiftTime.isEmpty
            && retouchGiftPhotoNum == 0
            && retouchGiftVideoNum == 0
    }
}

struct MultiTravelEntity: Decodable, Equatable {
    let materialList: [MultiTravelMaterialEntity]

    enum CodingKeys: String, CodingKey {
        case materialList = "material_list"
    }

    init(materialList: [MultiTravelMaterialEntity] = []) {
        self.materialList = materialList
    }

    var isEmpty: Bool {
        materialList.isEmpty
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        materialList = try container.decodeIfPresent([MultiTravelMaterialEntity].self, forKey: .materialList) ?? []
    }
}

struct MultiTravelMaterialEntity: Decodable, Equatable {
    let fileName: String
    let fileUrl: String
    let fileType: Int
    let fileSize: Int64
    let coverUrl: String

    enum CodingKeys: String, CodingKey {
        case fileName = "file_name"
        case fileUrl = "file_url"
        case fileType = "file_type"
        case fileSize = "file_size"
        case coverUrl = "cover_url"
    }
}

// MARK: - Store Order

struct StoreOrderListData: Decodable, Equatable {
    let total: Int
    let list: [StoreOrderItem]

    init(total: Int = 0, list: [StoreOrderItem] = []) {
        self.total = total
        self.list = list
    }
}

struct StoreOrderItem: Decodable, Equatable {
    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

    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
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber) ?? ""
        orderType = try container.decodeIfPresent(Int.self, forKey: .orderType) ?? 0
        orderTypeLabel = try container.decodeIfPresent(String.self, forKey: .orderTypeLabel) ?? ""
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
        orderStatus = try container.decodeIfPresent(Int.self, forKey: .orderStatus) ?? 0
        orderStatusName = try container.decodeIfPresent(String.self, forKey: .orderStatusName) ?? ""
        actualPayAmount = try container.decodeIfPresent(String.self, forKey: .actualPayAmount) ?? ""
        actualRefundAmount = try container.decodeIfPresent(String.self, forKey: .actualRefundAmount) ?? ""
        payTypeName = try container.decodeIfPresent(String.self, forKey: .payTypeName) ?? ""
        payTime = try container.decodeIfPresent(String.self, forKey: .payTime) ?? ""
        completeTime = try container.decodeIfPresent(String.self, forKey: .completeTime) ?? ""
        userId = try container.decodeIfPresent(Int.self, forKey: .userId) ?? 0
        phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? ""
        projectId = try container.decodeIfPresent(Int.self, forKey: .projectId) ?? 0
        projectName = try container.decodeIfPresent(String.self, forKey: .projectName) ?? ""
        remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? ""
    }
}

enum StoreOrderType {
    static let offlineProduct = 4
    static let multiTravelShoot = 19
}

// MARK: - Request Bodies

struct CancelRequest: Encodable {
    let orderNumber: String

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

struct OrderRefundRequest: Encodable {
    var orderNumber: String
    var refundType: Int
    var refundAmount: String
    var refundReason: String

    enum CodingKeys: String, CodingKey {
        case orderNumber = "order_number"
        case refundType = "refund_type"
        case refundAmount = "refund_amount"
        case refundReason = "refund_reason"
    }
}

struct SendGiftRequest: Encodable {
    let orderNumber: String
    let photoNum: Int
    let videoNum: Int

    enum CodingKeys: String, CodingKey {
        case orderNumber = "order_number"
        case photoNum = "photo_num"
        case videoNum = "video_num "
    }
}

struct StoreOrderRefundRequest: Encodable {
    let storeId: Int
    let orderNumber: String
    let refundType: Int
    let refundAmount: String
    let refundReason: String

    enum CodingKeys: String, CodingKey {
        case storeId = "store_id"
        case orderNumber = "order_number"
        case refundType = "refund_type"
        case refundAmount = "refund_amount"
        case refundReason = "refund_reason"
    }
}

struct StoreOrderVerifyRequest: Encodable {
    let method: String
    let userId: Int
    let orderNumber: String
    let time: String
    let storeId: Int

    enum CodingKeys: String, CodingKey {
        case method
        case userId = "user_id"
        case orderNumber = "order_number"
        case time
        case storeId = "store_id"
    }

    init(userId: Int, orderNumber: String, time: String, storeId: Int) {
        method = "order_verify"
        self.userId = userId
        self.orderNumber = orderNumber
        self.time = time
        self.storeId = storeId
    }
}

struct MultiTravelOrderVerifyRequest: Encodable {
    let method: String
    let orderNumber: String
    let userId: Int
    let time: Int64
    let checkSum: String
    let scenicSpotId: Int

    enum CodingKeys: String, CodingKey {
        case method
        case orderNumber = "order_number"
        case userId = "user_id"
        case time
        case checkSum = "check_sum"
        case scenicSpotId = "scenic_spot_id"
    }

    init(orderNumber: String, userId: Int, time: Int64, checkSum: String, scenicSpotId: Int) {
        method = "multi_verify"
        self.orderNumber = orderNumber
        self.userId = userId
        self.time = time
        self.checkSum = checkSum
        self.scenicSpotId = scenicSpotId
    }
}

// MARK: - Responses

struct OrderSignInResponse: Decodable, Equatable {
    let checkIn: Bool
    let orderNumber: String
    let projectName: String
    let photogName: String
    let scenicName: String
    let orderAmount: String
    let depositAmount: String
    let depositPayTime: String
    let userPhone: String

    enum CodingKeys: String, CodingKey {
        case checkIn = "check_in"
        case orderNumber = "order_number"
        case projectName = "project_name"
        case photogName = "photog_name"
        case scenicName = "scenic_name"
        case orderAmount = "order_amount"
        case depositAmount = "deposit_amount"
        case depositPayTime = "deposit_pay_time"
        case userPhone = "user_phone"
    }
}

struct WriteOffOrderResponse: Decodable, Equatable {
    let orderVerify: Bool
    let orderNumber: String
    let projectName: String
    let scenicName: String
    let orderAmount: String
    let payTime: String
    let userPhone: String
    let verifyTime: String

    enum CodingKeys: String, CodingKey {
        case orderVerify = "order_verify"
        case orderNumber = "order_number"
        case projectName = "project_name"
        case scenicName = "scenic_name"
        case orderAmount = "order_amount"
        case payTime = "pay_time"
        case userPhone = "user_phone"
        case verifyTime = "time"
    }
}

struct MultiTravelScenicSpotListResponse: Decodable {
    let scenicSpot: [MultiTravelScenicSpotItem]?

    enum CodingKeys: String, CodingKey {
        case scenicSpot = "scenic_spot"
    }

    var entityList: [MultiTravelScenicSpotEntity] {
        scenicSpot?.map { $0.toEntity() } ?? []
    }
}

struct MultiTravelScenicSpotItem: Decodable {
    let id: Int
    let name: String
    let cover: String
    let scenicName: String
    let scenicId: Int
    let distance: Double

    enum CodingKeys: String, CodingKey {
        case id, name, cover
        case scenicName = "scenic_name"
        case scenicId = "scenic_id"
        case distance
    }

    func toEntity() -> MultiTravelScenicSpotEntity {
        MultiTravelScenicSpotEntity(
            scenicSpotId: id,
            name: name,
            coverUrl: cover,
            scenicName: scenicName,
            distanceText: Self.formatDistance(distance),
            address: scenicName
        )
    }

    private static func formatDistance(_ distance: Double) -> String {
        if distance <= 0 { return "当前点位" }
        if distance < 1000 { return "\(Int(distance))米" }
        let km = distance / 1000
        let tenths = Int(km * 10)
        return "约\(tenths / 10).\(tenths % 10)公里"
    }
}

struct MultiTravelScenicSpotEntity: Equatable {
    let scenicSpotId: Int
    let name: String
    let coverUrl: String
    let scenicName: String
    let distanceText: String
    let address: String
}

struct MultiTravelShootHistoryResponse: Decodable, Equatable {
    let projectName: String
    let projectType: Int
    let projectTypeName: String
    let photogSpotList: [PhotogSpotItem]

    enum CodingKeys: String, CodingKey {
        case projectName = "project_name"
        case projectType = "project_type"
        case projectTypeName = "project_type_name"
        case photogSpotList = "photog_spot_list"
    }
}

struct PhotogSpotItem: Decodable, Equatable {
    let scenicSpotId: Int
    let photogUid: Int
    let scenicSpotName: String
    let photogNickname: String
    let photogName: String
    let files: [ShootHistoryFileItem]

    enum CodingKeys: String, CodingKey {
        case scenicSpotId = "scenic_spot_id"
        case photogUid = "photog_uid"
        case scenicSpotName = "scenic_spot_name"
        case photogNickname = "photog_nickname"
        case photogName = "photog_name"
        case files
    }

    var photographerDisplayName: String {
        photogNickname.isEmpty ? photogName : photogNickname
    }

    var imageUrls: [String] {
        files.compactMap { file in
            if !file.coverUrl.isEmpty { return file.coverUrl }
            if file.fileType == 2, !file.fileUrl.isEmpty { return file.fileUrl }
            return file.fileUrl.isEmpty ? nil : file.fileUrl
        }
    }
}

struct ShootHistoryFileItem: Decodable, Equatable {
    let fileName: String
    let fileUrl: String
    let fileType: Int
    let fileSize: Int64
    let coverUrl: String
    let uploadTime: String

    enum CodingKeys: String, CodingKey {
        case fileName = "file_name"
        case fileUrl = "file_url"
        case fileType = "file_type"
        case fileSize = "file_size"
        case coverUrl = "cover_url"
        case uploadTime = "upload_time"
    }
}

struct StoreOrderDetailData: Decodable, Equatable {
    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: StoreMultiTravelInfo?

    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"
    }
}

struct StoreMultiTravelInfo: Decodable, Equatable {
    let projectInfo: StoreProjectInfo?
    let shootingList: [StoreShootingListItem]

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

struct StoreProjectInfo: Decodable, Equatable {
    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"
    }
}

struct StoreShootingListItem: Decodable, Equatable {
    let scenicSpotId: Int
    let photogUid: Int
    let scenicSpotName: String
    let staffName: String
    let photogName: String
    let status: Int
    let startAvg: Float?
    let start: Float?

    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
    }

    var displayStaffName: String {
        staffName.isEmpty ? photogName : staffName
    }
}

// MARK: - Order Source

struct CooperativeSalerListResponse: Decodable {
    let total: Int
    let list: [CooperativeSalerEntity]
}

struct CooperativeSalerEntity: Decodable, Equatable {
    let saleUserId: Int
    let bindingId: Int
    let name: String
    let salerName: String
    let photogRemarkName: String
    let phone: String
    let salerPhone: String
    let commissionRate: Int
    let commissionRateLabel: String
    let bindTime: String
    let boundAt: String

    var displayId: Int { saleUserId }
    var displayName: String { OrderDisplayName.saleUser(remark: photogRemarkName, primary: name, fallback: salerName) }
    var displayPhone: String { salerPhone.isEmpty ? phone : salerPhone }
    var displayBindTime: String { bindTime.isEmpty ? boundAt : bindTime }
    var displayCommissionRate: String {
        if !commissionRateLabel.isEmpty { return commissionRateLabel }
        return "\(commissionRate)%"
    }

    enum CodingKeys: String, CodingKey {
        case saleUserId = "sale_user_id"
        case bindingId = "binding_id"
        case name
        case salerName = "saler_name"
        case photogRemarkName = "photog_remark_name"
        case phone
        case salerPhone = "saler_phone"
        case commissionRate = "commission_rate"
        case commissionRateLabel = "commission_rate_label"
        case bindTime = "bind_time"
        case boundAt = "bound_at"
    }

    init(
        saleUserId: Int = 0,
        bindingId: Int = 0,
        name: String = "",
        salerName: String = "",
        photogRemarkName: String = "",
        phone: String = "",
        salerPhone: String = "",
        commissionRate: Int = 0,
        commissionRateLabel: String = "",
        bindTime: String = "",
        boundAt: String = ""
    ) {
        self.saleUserId = saleUserId
        self.bindingId = bindingId
        self.name = name
        self.salerName = salerName
        self.photogRemarkName = photogRemarkName
        self.phone = phone
        self.salerPhone = salerPhone
        self.commissionRate = commissionRate
        self.commissionRateLabel = commissionRateLabel
        self.bindTime = bindTime
        self.boundAt = boundAt
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        saleUserId = try container.decodeIfPresent(Int.self, forKey: .saleUserId) ?? 0
        bindingId = try container.decodeIfPresent(Int.self, forKey: .bindingId) ?? 0
        name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
        salerName = try container.decodeIfPresent(String.self, forKey: .salerName) ?? ""
        photogRemarkName = try container.decodeIfPresent(String.self, forKey: .photogRemarkName) ?? ""
        phone = try container.decodeIfPresent(String.self, forKey: .phone) ?? ""
        salerPhone = try container.decodeIfPresent(String.self, forKey: .salerPhone) ?? ""
        commissionRate = try container.decodeIfPresent(Int.self, forKey: .commissionRate) ?? 0
        commissionRateLabel = try container.decodeIfPresent(String.self, forKey: .commissionRateLabel) ?? ""
        bindTime = try container.decodeIfPresent(String.self, forKey: .bindTime) ?? ""
        boundAt = try container.decodeIfPresent(String.self, forKey: .boundAt) ?? ""
    }

    func withRemarkName(_ remark: String) -> CooperativeSalerEntity {
        CooperativeSalerEntity(
            saleUserId: saleUserId,
            bindingId: bindingId,
            name: name,
            salerName: salerName,
            photogRemarkName: remark,
            phone: phone,
            salerPhone: salerPhone,
            commissionRate: commissionRate,
            commissionRateLabel: commissionRateLabel,
            bindTime: bindTime,
            boundAt: boundAt
        )
    }
}

/// 获客员分成比例修改记录分页响应。
struct CommissionRateLogListResponse: Decodable, Equatable {
    let list: [CommissionRateLogEntity]
    let total: Int
    let page: Int
    let pageSize: Int

    enum CodingKeys: String, CodingKey {
        case list
        case total
        case page
        case pageSize = "page_size"
    }
}

/// 单条获客员分成比例修改记录。
struct CommissionRateLogEntity: Decodable, Equatable, Hashable {
    let id: Int
    let bindingId: Int
    let saleUserId: Int
    let storeUserId: Int
    let beforeRate: Int
    let beforeRateLabel: String
    let afterRate: Int
    let afterRateLabel: String
    let operatorType: String
    let operatorId: Int
    let operatorName: String
    let remark: String
    let createdAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case bindingId = "binding_id"
        case saleUserId = "sale_user_id"
        case storeUserId = "store_user_id"
        case beforeRate = "before_rate"
        case beforeRateLabel = "before_rate_label"
        case afterRate = "after_rate"
        case afterRateLabel = "after_rate_label"
        case operatorType = "operator_type"
        case operatorId = "operator_id"
        case operatorName = "operator_name"
        case remark
        case createdAt = "created_at"
    }

    /// 修改前比例展示文本，优先使用服务端 label。
    var displayBeforeRate: String {
        let label = beforeRateLabel.trimmingCharacters(in: .whitespacesAndNewlines)
        return label.isEmpty ? "\(beforeRate)%" : label
    }

    /// 修改后比例展示文本，优先使用服务端 label。
    var displayAfterRate: String {
        let label = afterRateLabel.trimmingCharacters(in: .whitespacesAndNewlines)
        return label.isEmpty ? "\(afterRate)%" : label
    }

    /// 修改时间展示文本。
    var metadataLine: String {
        formattedCreatedAt
    }

    /// 去除首尾空白后的备注；空备注不展示。
    var displayRemark: String? {
        let value = remark.trimmingCharacters(in: .whitespacesAndNewlines)
        return value.isEmpty ? nil : value
    }

    private var formattedCreatedAt: String {
        let value = createdAt.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !value.isEmpty else { return "—" }
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "en_US_POSIX")
        formatter.timeZone = TimeZone(identifier: "Asia/Shanghai")
        for format in ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm"] {
            formatter.dateFormat = format
            if let date = formatter.date(from: value) {
                formatter.dateFormat = "yyyy-MM-dd HH:mm"
                return formatter.string(from: date)
            }
        }
        return value
    }
}

struct ReferralOrderListResponse: Decodable {
    let total: Int
    let list: [ReferralOrderEntity]
}

struct ReferralOrderEntity: Decodable, Equatable {
    let id: Int
    let referralOrderNo: String
    let orderNo: String
    let userMobile: String
    let remark: String
    let createdAt: String
    let status: Int
    let statusLabel: String
    let images: [String]

    var displayReferralOrderNo: String { referralOrderNo.isEmpty ? orderNo : referralOrderNo }

    enum CodingKeys: String, CodingKey {
        case id
        case referralOrderNo = "referral_order_no"
        case orderNo = "order_no"
        case userMobile = "user_mobile"
        case remark
        case createdAt = "created_at"
        case status
        case statusLabel = "status_label"
        case images
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0
        referralOrderNo = try container.decodeIfPresent(String.self, forKey: .referralOrderNo) ?? ""
        orderNo = try container.decodeIfPresent(String.self, forKey: .orderNo) ?? ""
        userMobile = try container.decodeIfPresent(String.self, forKey: .userMobile) ?? ""
        remark = try container.decodeIfPresent(String.self, forKey: .remark) ?? ""
        createdAt = try container.decodeIfPresent(String.self, forKey: .createdAt) ?? ""
        status = try container.decodeIfPresent(Int.self, forKey: .status) ?? 0
        statusLabel = try container.decodeIfPresent(String.self, forKey: .statusLabel) ?? ""
        images = (try? container.decode([String].self, forKey: .images)) ?? []
    }
}

// MARK: - Filters

enum PhotographerOrderFilter: CaseIterable {
    case all
    case paidDeposit
    case completed
    case refunded
    case needsRefinement
    case noRefinement

    var label: String {
        switch self {
        case .all: "全部"
        case .paidDeposit: "已付定金"
        case .completed: "已完成"
        case .refunded: "已退款"
        case .needsRefinement: "需要精修"
        case .noRefinement: "无需精修"
        }
    }

    var tabIndex: Int {
        switch self {
        case .all, .needsRefinement, .noRefinement: 0
        case .paidDeposit: 18
        case .completed: 30
        case .refunded: 50
        }
    }

    var rTabIndex: Int {
        switch self {
        case .needsRefinement: 1
        case .noRefinement: 2
        default: 0
        }
    }

    static func from(tabIndex: Int, rTabIndex: Int) -> PhotographerOrderFilter {
        switch (tabIndex, rTabIndex) {
        case (18, 0): .paidDeposit
        case (30, 0): .completed
        case (50, 0): .refunded
        case (0, 1): .needsRefinement
        case (0, 2): .noRefinement
        default: .all
        }
    }
}

enum StoreOrderFilter: CaseIterable {
    case all
    case paid
    case completed
    case refunded

    var label: String {
        switch self {
        case .all: "全部"
        case .paid: "已支付"
        case .completed: "已完成"
        case .refunded: "已退款"
        }
    }

    var tabIndex: Int {
        switch self {
        case .all: 0
        case .paid: 18
        case .completed: 30
        case .refunded: 50
        }
    }

    /// UI tab 18 映射 API order_status=20。
    var apiOrderStatus: Int {
        switch self {
        case .paid: 20
        default: tabIndex
        }
    }

    static func from(tabIndex: Int) -> StoreOrderFilter {
        switch tabIndex {
        case 18: .paid
        case 30: .completed
        case 50: .refunded
        default: .all
        }
    }
}

enum OrderTypeCode {
    static let photographerFollow = 11
    static let photographerFollowOfflineScan = 14
    static let multiTravel = 19
}

enum OrderDisplayName {
    static func saleUser(remark: String, primary: String, fallback: String = "") -> String {
        if !remark.isEmpty { return remark }
        if !primary.isEmpty { return primary }
        return fallback
    }
}

enum OrderListQueryBuilder {
    static let photographerPageSize = 10
    static let storePageSize = 20

    static func orderStatusQuery(tabIndex: Int) -> String? {
        tabIndex == 0 ? nil : String(tabIndex)
    }

    static func isRefinedQuery(rTabIndex: Int) -> Int? {
        rTabIndex == 0 ? nil : rTabIndex
    }

    static func storeOrderStatusQuery(tabIndex: Int) -> Int? {
        let status = StoreOrderFilter.from(tabIndex: tabIndex).apiOrderStatus
        return status == 0 ? nil : status
    }
}
