//
//  ProfileModels.swift
//  suixinkan
//

import Foundation

/// 用户资料响应实体，表示“我的”页面展示的账号基础信息。
struct UserInfoResponse: Decodable, Equatable {
    let avatar: String
    let realName: String
    let phone: String
    let nickname: String
    let roleName: String
    let status: Int
    let statusName: String

    enum CodingKeys: String, CodingKey {
        case avatar
        case realName = "real_name"
        case phone
        case nickname
        case roleName = "role_name"
        case status
        case statusName = "status_name"
    }

    init(
        avatar: String = "",
        realName: String = "",
        phone: String = "",
        nickname: String = "",
        roleName: String = "",
        status: Int = 0,
        statusName: String = ""
    ) {
        self.avatar = avatar
        self.realName = realName
        self.phone = phone
        self.nickname = nickname
        self.roleName = roleName
        self.status = status
        self.statusName = statusName
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        avatar = try container.decodeLossyString(forKey: .avatar)
        realName = try container.decodeLossyString(forKey: .realName)
        phone = try container.decodeLossyString(forKey: .phone)
        nickname = try container.decodeLossyString(forKey: .nickname)
        roleName = try container.decodeLossyString(forKey: .roleName)
        status = try container.decodeLossyInt(forKey: .status) ?? 0
        statusName = try container.decodeLossyString(forKey: .statusName)
    }
}

/// 用户资料更新请求实体，表示昵称、密码和头像修改参数。
struct UpdateInfoRequest: Encodable {
    let nickname: String?
    let password: String?
    let avatar: String?
}

/// 摄影师个人空间配置响应，对齐 Android `ProfileInfoResponse`。
struct PhotographerProfileInfoResponse: Decodable, Equatable {
    let id: Int
    let scenicId: Int
    let photogUid: Int
    let realName: String
    let nickname: String
    let avatar: String
    let scenicCertification: [String]
    let description: String
    let attrLabel: [String]
    let shootLabel: [String]
    let cameraDevice: [String]
    let businessStartTime: String
    let businessEndTime: String
    let holidayStartTime: String
    let holidayEndTime: String
    let businessRange: [Int]
    let acceptOrderStatus: Int
    let schedule: [String: [PhotographerSchedule]]
    let virtualPhone: String

    enum CodingKeys: String, CodingKey {
        case id
        case scenicId = "scenic_id"
        case photogUid = "photog_uid"
        case realName = "real_name"
        case nickname
        case avatar
        case scenicCertification = "scenic_certification"
        case description
        case attrLabel = "attr_label"
        case shootLabel = "shoot_label"
        case cameraDevice = "camera_device"
        case businessStartTime = "business_start_time"
        case businessEndTime = "business_end_time"
        case holidayStartTime = "holiday_start_time"
        case holidayEndTime = "holiday_end_time"
        case businessRange = "business_range"
        case acceptOrderStatus = "accept_order_status"
        case schedule
        case virtualPhone = "virtual_phone"
    }

    init(
        id: Int = 0,
        scenicId: Int = 0,
        photogUid: Int = 0,
        realName: String = "",
        nickname: String = "",
        avatar: String = "",
        scenicCertification: [String] = [],
        description: String = "",
        attrLabel: [String] = [],
        shootLabel: [String] = [],
        cameraDevice: [String] = [],
        businessStartTime: String = "",
        businessEndTime: String = "",
        holidayStartTime: String = "",
        holidayEndTime: String = "",
        businessRange: [Int] = [],
        acceptOrderStatus: Int = 0,
        schedule: [String: [PhotographerSchedule]] = [:],
        virtualPhone: String = ""
    ) {
        self.id = id
        self.scenicId = scenicId
        self.photogUid = photogUid
        self.realName = realName
        self.nickname = nickname
        self.avatar = avatar
        self.scenicCertification = scenicCertification
        self.description = description
        self.attrLabel = attrLabel
        self.shootLabel = shootLabel
        self.cameraDevice = cameraDevice
        self.businessStartTime = businessStartTime
        self.businessEndTime = businessEndTime
        self.holidayStartTime = holidayStartTime
        self.holidayEndTime = holidayEndTime
        self.businessRange = businessRange
        self.acceptOrderStatus = acceptOrderStatus
        self.schedule = schedule
        self.virtualPhone = virtualPhone
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id) ?? 0
        scenicId = try container.decodeLossyInt(forKey: .scenicId) ?? 0
        photogUid = try container.decodeLossyInt(forKey: .photogUid) ?? 0
        realName = try container.decodeLossyString(forKey: .realName)
        nickname = try container.decodeLossyString(forKey: .nickname)
        avatar = try container.decodeLossyString(forKey: .avatar)
        scenicCertification = try container.decodeLossyStringArray(forKey: .scenicCertification)
        description = try container.decodeLossyString(forKey: .description)
        attrLabel = try container.decodeLossyStringArray(forKey: .attrLabel)
        shootLabel = try container.decodeLossyStringArray(forKey: .shootLabel)
        cameraDevice = try container.decodeLossyStringArray(forKey: .cameraDevice)
        businessStartTime = try container.decodeLossyString(forKey: .businessStartTime)
        businessEndTime = try container.decodeLossyString(forKey: .businessEndTime)
        holidayStartTime = try container.decodeLossyString(forKey: .holidayStartTime)
        holidayEndTime = try container.decodeLossyString(forKey: .holidayEndTime)
        businessRange = try container.decodeLossyIntArray(forKey: .businessRange)
        acceptOrderStatus = try container.decodeLossyInt(forKey: .acceptOrderStatus) ?? 0
        schedule = try container.decodeScheduleMap(forKey: .schedule)
        virtualPhone = try container.decodeLossyString(forKey: .virtualPhone)
    }
}

/// 摄影师日程实体，对齐 Android `ScheduleEntity`。
struct PhotographerSchedule: Decodable, Equatable, Hashable, Sendable {
    let id: Int
    let name: String
    let remark: String
    let startTime: String
    let endTime: String
    let scheduleDate: String
    let orderNumber: String?
    let userNickname: String?
    let userPhone: String?
    let orderStatus: Int?
    let orderStatusName: String?

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case remark
        case startTime = "start_time"
        case endTime = "end_time"
        case scheduleDate = "schedule_date"
        case orderNumber = "order_number"
        case userNickname = "user_nickname"
        case userPhone = "user_phone"
        case orderStatus = "order_status"
        case orderStatusName = "order_status_name"
    }

    init(
        id: Int = 0,
        name: String = "",
        remark: String = "",
        startTime: String = "",
        endTime: String = "",
        scheduleDate: String = "",
        orderNumber: String? = nil,
        userNickname: String? = nil,
        userPhone: String? = nil,
        orderStatus: Int? = nil,
        orderStatusName: String? = nil
    ) {
        self.id = id
        self.name = name
        self.remark = remark
        self.startTime = startTime
        self.endTime = endTime
        self.scheduleDate = scheduleDate
        self.orderNumber = orderNumber
        self.userNickname = userNickname
        self.userPhone = userPhone
        self.orderStatus = orderStatus
        self.orderStatusName = orderStatusName
    }

    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)
        remark = try container.decodeLossyString(forKey: .remark)
        startTime = try container.decodeLossyString(forKey: .startTime)
        endTime = try container.decodeLossyString(forKey: .endTime)
        scheduleDate = try container.decodeLossyString(forKey: .scheduleDate)
        orderNumber = try container.decodeIfPresent(String.self, forKey: .orderNumber)
        userNickname = try container.decodeIfPresent(String.self, forKey: .userNickname)
        userPhone = try container.decodeIfPresent(String.self, forKey: .userPhone)
        orderStatus = try container.decodeLossyInt(forKey: .orderStatus)
        orderStatusName = try container.decodeIfPresent(String.self, forKey: .orderStatusName)
    }
}

/// 摄影师个人空间配置更新请求，对齐 Android `UpdateProfileInfoRequest`。
struct UpdatePhotographerProfileInfoRequest: Encodable, Equatable {
    let scenicId: String
    let description: String
    let cameraDevice: [String]
    let attrLabel: [String]
    let shootLabel: [String]
    let businessStartTime: String
    let businessEndTime: String
    let holidayStartTime: String
    let holidayEndTime: String
    let businessRange: [Int]
    let acceptOrderStatus: Int

    enum CodingKeys: String, CodingKey {
        case scenicId = "scenic_id"
        case description
        case cameraDevice = "camera_device"
        case attrLabel = "attr_label"
        case shootLabel = "shoot_label"
        case businessStartTime = "business_start_time"
        case businessEndTime = "business_end_time"
        case holidayStartTime = "holiday_start_time"
        case holidayEndTime = "holiday_end_time"
        case businessRange = "business_range"
        case acceptOrderStatus = "accept_order_status"
    }
}

/// 摄影师新增日程请求，对齐 Android `AddScheduleRequest`。
struct AddPhotographerScheduleRequest: Encodable, Equatable {
    var scenicId: String
    var name: String
    var remark: String
    var startTime: String
    var endTime: String
    var scheduleDate: String
    var orderNumber: String?

    enum CodingKeys: String, CodingKey {
        case scenicId = "scenic_id"
        case name
        case remark
        case startTime = "start_time"
        case endTime = "end_time"
        case scheduleDate = "schedule_date"
        case orderNumber = "order_number"
    }
}

/// 摄影师删除日程请求，对齐 Android `ScheduleDeleteRequest`。
struct PhotographerScheduleDeleteRequest: Encodable, Equatable {
    let id: Int
}

/// 实名认证响应实体，包裹当前用户的认证详情。
struct RealNameInfoResponse: Decodable, Equatable {
    let realNameInfo: RealNameInfo?

    enum CodingKeys: String, CodingKey {
        case realNameInfo = "real_name_info"
    }
}

/// 实名认证信息实体，表示当前用户实名审核状态和失败原因。
struct RealNameInfo: Decodable, Equatable {
    let realName: String
    let idCardNo: String
    let auditStatus: Int
    let auditStatusText: String?
    let rejectReason: String?
    let startDate: String?
    let endDate: String?
    let isLongValid: Bool
    let frontUrl: String?
    let backUrl: String?
    let auditorId: Int?
    let auditor: RealNameAuditor?
    let auditAt: String?

    var verified: Bool {
        auditStatus == 2
    }

    enum CodingKeys: String, CodingKey {
        case realName = "real_name"
        case idCardNo = "id_card_no"
        case auditStatus = "audit_status"
        case auditStatusText = "audit_status_text"
        case rejectReason = "reject_reason"
        case startDate = "start_date"
        case endDate = "end_date"
        case isLongValid = "is_long_valid"
        case frontUrl = "front_url"
        case backUrl = "back_url"
        case auditorId = "auditor_id"
        case auditor
        case auditAt = "audit_at"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        realName = try container.decodeLossyString(forKey: .realName)
        idCardNo = try container.decodeLossyString(forKey: .idCardNo)
        auditStatus = try container.decodeLossyInt(forKey: .auditStatus) ?? 0
        auditStatusText = try container.decodeIfPresent(String.self, forKey: .auditStatusText)
        rejectReason = try container.decodeIfPresent(String.self, forKey: .rejectReason)
        startDate = try container.decodeIfPresent(String.self, forKey: .startDate)
        endDate = try container.decodeIfPresent(String.self, forKey: .endDate)
        isLongValid = (try container.decodeLossyInt(forKey: .isLongValid) ?? 0) == 1
        frontUrl = try container.decodeIfPresent(String.self, forKey: .frontUrl)
        backUrl = try container.decodeIfPresent(String.self, forKey: .backUrl)
        auditorId = try container.decodeLossyInt(forKey: .auditorId)
        auditor = try container.decodeIfPresent(RealNameAuditor.self, forKey: .auditor)
        auditAt = try container.decodeIfPresent(String.self, forKey: .auditAt)
    }
}

/// 实名认证审核人实体，表示后台审核人的基础展示信息。
struct RealNameAuditor: Decodable, Equatable {
    let id: Int
    let name: String

    enum CodingKeys: String, CodingKey {
        case id
        case name
    }

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

/// 实名认证提交请求实体，表示用户填写的姓名、证件号、短信和证件图片。
struct RealNameAuthRequest: Encodable, Equatable {
    let realName: String
    let idCardNo: String
    let smsVerifyCode: String
    let startDate: String
    let endDate: String
    let isLongValid: Int
    let frontUrl: String
    let backUrl: String

    enum CodingKeys: String, CodingKey {
        case realName = "real_name"
        case idCardNo = "id_card_no"
        case smsVerifyCode = "sms_verify_code"
        case startDate = "start_date"
        case endDate = "end_date"
        case isLongValid = "is_long_valid"
        case frontUrl = "front_url"
        case backUrl = "back_url"
    }
}

/// 银行卡信息响应实体。
struct BankCardInfoResponse: Decodable, Equatable {
    let bankCard: BankCardInfo?

    enum CodingKeys: String, CodingKey {
        case bankCard = "bank_card"
    }
}

/// 银行卡信息实体，表示提现设置与审核状态。
struct BankCardInfo: Decodable, Equatable {
    let realName: String
    let bankName: String
    let branchName: String
    let cardNumber: String
    let frontUrl: String?
    let backUrl: String?
    let provinceCode: String?
    let cityCode: String?
    let rejectReason: String
    let auditStatus: Int
    let auditStatusLabel: String
    let auditAt: String?
    let auditorId: Int?
    let auditor: RealNameAuditor?

    enum CodingKeys: String, CodingKey {
        case realName = "real_name"
        case bankName = "bank_name"
        case branchName = "branch_name"
        case cardNumber = "card_number"
        case frontUrl = "front_url"
        case backUrl = "back_url"
        case provinceCode = "province_code"
        case cityCode = "city_code"
        case rejectReason = "reject_reason"
        case auditStatus = "audit_status"
        case auditStatusLabel = "audit_status_label"
        case auditAt = "audit_at"
        case auditorId = "auditor_id"
        case auditor
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        realName = try container.decodeLossyString(forKey: .realName)
        bankName = try container.decodeLossyString(forKey: .bankName)
        branchName = try container.decodeLossyString(forKey: .branchName)
        cardNumber = try container.decodeLossyString(forKey: .cardNumber)
        frontUrl = try container.decodeIfPresent(String.self, forKey: .frontUrl)
        backUrl = try container.decodeIfPresent(String.self, forKey: .backUrl)
        provinceCode = try container.decodeIfPresent(String.self, forKey: .provinceCode)
        cityCode = try container.decodeIfPresent(String.self, forKey: .cityCode)
        rejectReason = try container.decodeLossyString(forKey: .rejectReason)
        auditStatus = try container.decodeLossyInt(forKey: .auditStatus) ?? 0
        auditStatusLabel = try container.decodeLossyString(forKey: .auditStatusLabel)
        auditAt = try container.decodeIfPresent(String.self, forKey: .auditAt)
        auditorId = try container.decodeLossyInt(forKey: .auditorId)
        auditor = try container.decodeIfPresent(RealNameAuditor.self, forKey: .auditor)
    }
}

/// 银行列表响应实体。
struct BankListResponse: Decodable, Equatable {
    let banks: [String]

    enum CodingKeys: String, CodingKey {
        case banks
    }

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

/// 省市区响应实体。
struct AreasResponse: Decodable, Equatable, Identifiable {
    let id: Int
    let code: String
    let name: String
    let children: [AreasResponse]

    enum CodingKeys: String, CodingKey {
        case id
        case code
        case name
        case children
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decodeLossyInt(forKey: .id)
            ?? container.decodeLossyInt(forKey: .code)
            ?? 0
        let decodedCode = try container.decodeLossyString(forKey: .code)
            .trimmingCharacters(in: .whitespacesAndNewlines)
        code = decodedCode.isEmpty ? "\(id)" : decodedCode
        name = try container.decodeLossyString(forKey: .name)
        children = try container.decodeIfPresent([AreasResponse].self, forKey: .children) ?? []
    }

    init(id: Int, code: String, name: String, children: [AreasResponse] = []) {
        self.id = id
        self.code = code
        self.name = name
        self.children = children
    }
}

/// 更新银行卡信息请求实体。
struct UpdateBankInfoRequest: Encodable, Equatable {
    let realName: String
    let cardNumber: String
    let bankName: String
    let branchName: String
    let frontUrl: String
    let backUrl: String
    let provinceCode: String
    let cityCode: String
    let smsVerifyCode: String

    enum CodingKeys: String, CodingKey {
        case realName = "real_name"
        case cardNumber = "card_number"
        case bankName = "bank_name"
        case branchName = "branch_name"
        case frontUrl = "front_url"
        case backUrl = "back_url"
        case provinceCode = "province_code"
        case cityCode = "city_code"
        case smsVerifyCode = "sms_verify_code"
    }
}

/// 个人信息页校验错误实体。
enum ProfileValidationError: LocalizedError, Equatable {
    case emptyNickname
    case shortPassword

    var errorDescription: String? {
        switch self {
        case .emptyNickname:
            "请输入昵称"
        case .shortPassword:
            "密码至少 6 位"
        }
    }
}

private extension KeyedDecodingContainer {
    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 ""
    }

    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
    }

    func decodeLossyStringArray(forKey key: Key) throws -> [String] {
        if let values = try? decodeIfPresent([String].self, forKey: key) {
            return values
        }
        if let values = try? decodeIfPresent([Int].self, forKey: key) {
            return values.map(String.init)
        }
        if let value = try? decodeIfPresent(String.self, forKey: key) {
            let text = value.trimmingCharacters(in: .whitespacesAndNewlines)
            return text.isEmpty ? [] : [text]
        }
        return []
    }

    func decodeLossyIntArray(forKey key: Key) throws -> [Int] {
        if let values = try? decodeIfPresent([Int].self, forKey: key) {
            return values
        }
        if let values = try? decodeIfPresent([String].self, forKey: key) {
            return values.compactMap { Int($0.trimmingCharacters(in: .whitespacesAndNewlines)) }
        }
        if let value = try? decodeIfPresent(Int.self, forKey: key) {
            return [value]
        }
        return []
    }

    func decodeScheduleMap(forKey key: Key) throws -> [String: [PhotographerSchedule]] {
        if let values = try? decodeIfPresent([String: [PhotographerSchedule]].self, forKey: key) {
            return values
        }
        if let values = try? decodeIfPresent([PhotographerSchedule].self, forKey: key) {
            return Dictionary(grouping: values) { $0.scheduleDate }
                .filter { !$0.key.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty }
        }
        return [:]
    }
}
