//
//  CooperationOrderModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/29.
//

import Foundation

extension String {
    /// 非空时返回自身，否则返回 fallback。
    func cooperationNonEmptyOrDefault(_ fallback: String) -> String {
        let trimmed = trimmingCharacters(in: .whitespacesAndNewlines)
        return trimmed.isEmpty ? fallback : trimmed
    }
}

/// 带客单状态更新请求体。
struct ReferralOrderStatusUpdateRequest: Encodable {
    let status: Int
}

/// 合作获客员列表响应。
struct CooperativeSalerListPayload: Decodable {
    let total: Int
    let list: [CooperativeSaler]
    let page: Int
    let pageSize: Int

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

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        total = try container.decodeLossyInt(forKey: .total) ?? 0
        list = try container.decodeIfPresent([CooperativeSaler].self, forKey: .list) ?? []
        page = try container.decodeLossyInt(forKey: .page) ?? 0
        pageSize = try container.decodeLossyInt(forKey: .pageSize) ?? 0
    }
}

/// 合作获客员实体。
struct CooperativeSaler: Decodable, Identifiable {
    let saleUserId: Int
    let bindingId: Int
    let name: String
    let salerName: String
    let phone: String
    let phoneMasked: String
    let salerPhone: String
    let shareCode: String
    let storeId: Int
    let bindTime: String
    let boundAt: String

    var id: Int { saleUserId }

    var displayId: Int { saleUserId }

    var displayName: String {
        name.cooperationNonEmptyOrDefault(salerName)
    }

    var displayPhone: String {
        salerPhone.cooperationNonEmptyOrDefault(phone)
    }

    var displayBindTime: String {
        bindTime.cooperationNonEmptyOrDefault(boundAt)
    }

    enum CodingKeys: String, CodingKey {
        case saleUserId = "sale_user_id"
        case bindingId = "binding_id"
        case name
        case salerName = "saler_name"
        case phone
        case phoneMasked = "phone_masked"
        case salerPhone = "saler_phone"
        case shareCode = "share_code"
        case storeId = "store_id"
        case bindTime = "bind_time"
        case boundAt = "bound_at"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        saleUserId = try container.decodeLossyInt(forKey: .saleUserId) ?? 0
        bindingId = try container.decodeLossyInt(forKey: .bindingId) ?? 0
        name = try container.decodeLossyString(forKey: .name)
        salerName = try container.decodeLossyString(forKey: .salerName)
        phone = try container.decodeLossyString(forKey: .phone)
        phoneMasked = try container.decodeLossyString(forKey: .phoneMasked)
        salerPhone = try container.decodeLossyString(forKey: .salerPhone)
        shareCode = try container.decodeLossyString(forKey: .shareCode)
        storeId = try container.decodeLossyInt(forKey: .storeId) ?? 0
        bindTime = try container.decodeLossyString(forKey: .bindTime)
        boundAt = try container.decodeLossyString(forKey: .boundAt)
    }
}

/// 获客员邀请信息，用于绑定确认页展示。
struct SaleUserInviteInfo: Decodable {
    let saleUserId: Int
    let isBound: Bool
    let photographerName: String
    let photographerPhone: String
    let salerName: String
    let salerPhone: String
    let shareCode: String
    let subtitle: String
    let title: String

    enum CodingKeys: String, CodingKey {
        case saleUserId = "sale_user_id"
        case isBound = "is_bound"
        case photographerName = "photographer_name"
        case photographerPhone = "photographer_phone"
        case salerName = "saler_name"
        case salerPhone = "saler_phone"
        case shareCode = "share_code"
        case subtitle
        case title
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        saleUserId = try container.decodeLossyInt(forKey: .saleUserId) ?? 0
        isBound = try container.decodeLossyBool(forKey: .isBound) ?? false
        photographerName = try container.decodeLossyString(forKey: .photographerName)
        photographerPhone = try container.decodeLossyString(forKey: .photographerPhone)
        salerName = try container.decodeLossyString(forKey: .salerName)
        salerPhone = try container.decodeLossyString(forKey: .salerPhone)
        shareCode = try container.decodeLossyString(forKey: .shareCode)
        subtitle = try container.decodeLossyString(forKey: .subtitle)
        title = try container.decodeLossyString(forKey: .title)
    }

    init(
        saleUserId: Int,
        isBound: Bool = false,
        photographerName: String = "",
        photographerPhone: String = "",
        salerName: String = "",
        salerPhone: String = "",
        shareCode: String = "",
        subtitle: String = "",
        title: String = ""
    ) {
        self.saleUserId = saleUserId
        self.isBound = isBound
        self.photographerName = photographerName
        self.photographerPhone = photographerPhone
        self.salerName = salerName
        self.salerPhone = salerPhone
        self.shareCode = shareCode
        self.subtitle = subtitle
        self.title = title
    }
}

/// 获客员线索实体。
struct ReferralLead: Decodable, Identifiable {
    let id: Int
    let leadId: Int
    let userPhone: String
    let userMobileMasked: String
    let phone: String
    let remark: String
    let createdAt: String
    let imageUrls: [String]
    let images: [String]

    var displayId: String {
        let value = leadId > 0 ? leadId : id
        return "\(value)"
    }

    var displayPhone: String {
        userMobileMasked.cooperationNonEmptyOrDefault(userPhone.cooperationNonEmptyOrDefault(phone))
    }

    var displayImages: [String] {
        imageUrls.isEmpty ? images : imageUrls
    }

    enum CodingKeys: String, CodingKey {
        case id
        case leadId = "lead_id"
        case userPhone = "user_phone"
        case userMobileMasked = "user_mobile_masked"
        case phone
        case remark
        case createdAt = "created_at"
        case imageUrls = "image_urls"
        case images
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        leadId = try container.decodeLossyInt(forKey: .leadId) ?? 0
        userPhone = try container.decodeLossyString(forKey: .userPhone)
        userMobileMasked = try container.decodeLossyString(forKey: .userMobileMasked)
        phone = try container.decodeLossyString(forKey: .phone)
        remark = try container.decodeLossyString(forKey: .remark)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        imageUrls = try container.decodeIfPresent([String].self, forKey: .imageUrls) ?? []
        images = try container.decodeIfPresent([String].self, forKey: .images) ?? []
    }
}

/// 获客订单实体。
struct AcquisitionOrder: Decodable, Identifiable {
    let id: Int
    let orderNumber: String
    let projectName: String
    let orderType: Int
    let orderTypeLabel: String
    let orderAmount: String
    let amount: String
    let actualPayAmount: String
    let createdAt: String
    let customerPhone: String
    let userMobileMasked: String
    let phone: String
    let orderStatusName: String
    let status: String
    let saleUserName: String
    let partnerName: String
    let partnerRole: String
    let remark: String
    let imageUrls: [String]

    var displayAmount: String {
        orderAmount.cooperationNonEmptyOrDefault(amount.cooperationNonEmptyOrDefault(actualPayAmount))
    }

    var displayCustomerPhone: String {
        userMobileMasked.cooperationNonEmptyOrDefault(customerPhone.cooperationNonEmptyOrDefault(phone))
    }

    var displayStatus: String {
        orderStatusName.cooperationNonEmptyOrDefault(status)
    }

    var displayPartner: String {
        if !saleUserName.isEmpty { return saleUserName }
        if !partnerName.isEmpty { return partnerName }
        return ""
    }

    enum CodingKeys: String, CodingKey {
        case id
        case orderNumber = "order_number"
        case projectName = "project_name"
        case orderType = "order_type"
        case orderTypeLabel = "order_type_label"
        case orderAmount = "order_amount"
        case amount
        case actualPayAmount = "actual_pay_amount"
        case createdAt = "created_at"
        case customerPhone = "customer_phone"
        case userMobileMasked = "user_mobile_masked"
        case phone
        case orderStatusName = "order_status_name"
        case status
        case saleUserName = "sale_user_name"
        case partnerName = "partner_name"
        case partnerRole = "partner_role"
        case remark
        case imageUrls = "image_urls"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        orderNumber = try container.decodeLossyString(forKey: .orderNumber)
        projectName = try container.decodeLossyString(forKey: .projectName)
        orderType = try container.decodeLossyInt(forKey: .orderType) ?? 0
        orderTypeLabel = try container.decodeLossyString(forKey: .orderTypeLabel)
        orderAmount = try container.decodeLossyString(forKey: .orderAmount)
        amount = try container.decodeLossyString(forKey: .amount)
        actualPayAmount = try container.decodeLossyString(forKey: .actualPayAmount)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        customerPhone = try container.decodeLossyString(forKey: .customerPhone)
        userMobileMasked = try container.decodeLossyString(forKey: .userMobileMasked)
        phone = try container.decodeLossyString(forKey: .phone)
        orderStatusName = try container.decodeLossyString(forKey: .orderStatusName)
        status = try container.decodeLossyString(forKey: .status)
        saleUserName = try container.decodeLossyString(forKey: .saleUserName)
        partnerName = try container.decodeLossyString(forKey: .partnerName)
        partnerRole = try container.decodeLossyString(forKey: .partnerRole)
        remark = try container.decodeLossyString(forKey: .remark)
        imageUrls = try container.decodeIfPresent([String].self, forKey: .imageUrls) ?? []
    }
}

/// 带客单列表中的获客员摘要。
struct ReferralOrderSaler: Decodable {
    let saleUserId: Int
    let salerName: String
    let salerPhone: String

    enum CodingKeys: String, CodingKey {
        case saleUserId = "sale_user_id"
        case salerName = "saler_name"
        case salerPhone = "saler_phone"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        saleUserId = try container.decodeLossyInt(forKey: .saleUserId) ?? 0
        salerName = try container.decodeLossyString(forKey: .salerName)
        salerPhone = try container.decodeLossyString(forKey: .salerPhone)
    }
}

/// 可绑定带客单列表响应。
struct ReferralOrderListPayload: Decodable {
    let total: Int
    let list: [ReferralOrder]
    let page: Int
    let pageSize: Int
    let saler: ReferralOrderSaler?

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

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        total = try container.decodeLossyInt(forKey: .total) ?? 0
        list = try container.decodeIfPresent([ReferralOrder].self, forKey: .list) ?? []
        page = try container.decodeLossyInt(forKey: .page) ?? 0
        pageSize = try container.decodeLossyInt(forKey: .pageSize) ?? 0
        saler = try container.decodeIfPresent(ReferralOrderSaler.self, forKey: .saler)
    }
}

/// 可绑定带客单实体。
struct ReferralOrder: Decodable, Identifiable {
    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.cooperationNonEmptyOrDefault(orderNo)
    }

    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.decodeLossyInt(forKey: .id) ?? 0
        referralOrderNo = try container.decodeLossyString(forKey: .referralOrderNo)
        orderNo = try container.decodeLossyString(forKey: .orderNo)
        userMobile = try container.decodeLossyString(forKey: .userMobile)
        remark = try container.decodeLossyString(forKey: .remark)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusLabel = try container.decodeLossyString(forKey: .statusLabel)
        images = try container.decodeIfPresent([String].self, forKey: .images) ?? []
    }
}

/// listv2 已绑定订单来源 referral_order。
struct BoundReferralOrder: Decodable, Equatable, Hashable {
    let id: Int
    let orderNo: String
    let saleUserId: Int
    let salerName: 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

    enum CodingKeys: String, CodingKey {
        case id
        case orderNo = "order_no"
        case saleUserId = "sale_user_id"
        case salerName = "saler_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"
    }

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

/// 扫码绑定获客员 Sheet 标识。
struct BindAcquirerSheetItem: Identifiable {
    let saleUserId: Int
    var id: Int { saleUserId }
}

/// 合作订单模块内部导航路由。
enum CooperationOrderRoute: Hashable {
    case acquirer
    case bind(saleUserId: Int)
}

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