//
//  ScenicQueueSettingsModels.swift
//  suixinkan
//
//  Created by Codex on 2026/6/25.
//

import Foundation

/// 排队设置响应。
struct ScenicQueueSettingData: Decodable, Equatable {
    let exists: Bool
    let setting: ScenicQueueSettingItem?
    let time: String?

    enum CodingKeys: String, CodingKey {
        case exists
        case setting
        case time
    }

    /// 创建排队设置响应。
    init(exists: Bool = false, setting: ScenicQueueSettingItem? = nil, time: String? = nil) {
        self.exists = exists
        self.setting = setting
        self.time = time
    }

    /// 宽松解码排队设置响应。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        exists = try container.decodeLossyBool(forKey: .exists) ?? false
        setting = try container.decodeIfPresent(ScenicQueueSettingItem.self, forKey: .setting)
        time = try container.decodeLossyString(forKey: .time)
    }
}

/// 排队设置实体。
struct ScenicQueueSettingItem: Decodable, Equatable {
    let id: Int64?
    let scenicId: Int?
    let scenicSpotId: Int?
    let scenicSpotName: String
    let photoEstimateMin: Int
    let photoEstimateSec: Int
    let firstNoticeThresholdPos: Int
    let firstNoticeSmsEnabled: Int
    let firstNoticeCallEnabled: Int
    let secondNoticeThresholdPos: Int
    let secondNoticeSmsEnabled: Int
    let secondNoticeCallEnabled: Int
    let countdownBroadcastIntervalSec: Int
    let countdownReadableThresholdSec: Int
    let queueDistanceMeter: Int
    let queueTakeLimit: Int
    let missCallRequeueOffset: Int
    let showStartShootButton: Int?
    let autoCallNextCount: Int?
    let businessStartTime: String
    let businessEndTime: String
    let status: Int
    let remark: String
    let voiceBroadcasts: [ScenicQueueVoiceBroadcastItem]
    let createdAt: String
    let updatedAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case scenicId = "scenic_id"
        case scenicSpotId = "scenic_spot_id"
        case scenicSpotName = "scenic_spot_name"
        case photoEstimateMin = "photo_estimate_min"
        case photoEstimateSec = "photo_estimate_sec"
        case firstNoticeThresholdPos = "first_notice_threshold_pos"
        case firstNoticeSmsEnabled = "first_notice_sms_enabled"
        case firstNoticeCallEnabled = "first_notice_call_enabled"
        case secondNoticeThresholdPos = "second_notice_threshold_pos"
        case secondNoticeSmsEnabled = "second_notice_sms_enabled"
        case secondNoticeCallEnabled = "second_notice_call_enabled"
        case countdownBroadcastIntervalSec = "countdown_broadcast_interval_sec"
        case countdownReadableThresholdSec = "countdown_readable_threshold_sec"
        case queueDistanceMeter = "queue_distance_meter"
        case queueTakeLimit = "queue_take_limit"
        case missCallRequeueOffset = "miss_call_requeue_offset"
        case showStartShootButton = "show_start_shoot_button"
        case autoCallNextCount = "auto_call_next_count"
        case businessStartTime = "business_start_time"
        case businessEndTime = "business_end_time"
        case status
        case remark
        case voiceBroadcasts = "voice_broadcasts"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }

    /// 宽松解码排队设置实体。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = (try container.decodeLossyInt(forKey: .id)).map(Int64.init)
        scenicId = try container.decodeLossyInt(forKey: .scenicId)
        scenicSpotId = try container.decodeLossyInt(forKey: .scenicSpotId)
        scenicSpotName = try container.decodeLossyString(forKey: .scenicSpotName)
        photoEstimateMin = try container.decodeLossyInt(forKey: .photoEstimateMin) ?? 0
        photoEstimateSec = try container.decodeLossyInt(forKey: .photoEstimateSec) ?? 0
        firstNoticeThresholdPos = try container.decodeLossyInt(forKey: .firstNoticeThresholdPos) ?? 0
        firstNoticeSmsEnabled = try container.decodeLossyInt(forKey: .firstNoticeSmsEnabled) ?? 0
        firstNoticeCallEnabled = try container.decodeLossyInt(forKey: .firstNoticeCallEnabled) ?? 0
        secondNoticeThresholdPos = try container.decodeLossyInt(forKey: .secondNoticeThresholdPos) ?? 0
        secondNoticeSmsEnabled = try container.decodeLossyInt(forKey: .secondNoticeSmsEnabled) ?? 0
        secondNoticeCallEnabled = try container.decodeLossyInt(forKey: .secondNoticeCallEnabled) ?? 0
        countdownBroadcastIntervalSec = try container.decodeLossyInt(forKey: .countdownBroadcastIntervalSec) ?? 50
        countdownReadableThresholdSec = try container.decodeLossyInt(forKey: .countdownReadableThresholdSec) ?? 15
        queueDistanceMeter = try container.decodeLossyInt(forKey: .queueDistanceMeter) ?? 0
        queueTakeLimit = try container.decodeLossyInt(forKey: .queueTakeLimit) ?? 0
        missCallRequeueOffset = try container.decodeLossyInt(forKey: .missCallRequeueOffset) ?? 1
        showStartShootButton = try container.decodeLossyInt(forKey: .showStartShootButton)
        autoCallNextCount = try container.decodeLossyInt(forKey: .autoCallNextCount)
        businessStartTime = try container.decodeLossyString(forKey: .businessStartTime)
        businessEndTime = try container.decodeLossyString(forKey: .businessEndTime)
        status = try container.decodeLossyInt(forKey: .status) ?? 1
        remark = try container.decodeLossyString(forKey: .remark)
        voiceBroadcasts = (try? container.decodeIfPresent([ScenicQueueVoiceBroadcastItem].self, forKey: .voiceBroadcasts)) ?? []
        createdAt = try container.decodeLossyString(forKey: .createdAt)
        updatedAt = try container.decodeLossyString(forKey: .updatedAt)
    }
}

/// 排队语音播报预设。
struct ScenicQueueVoiceBroadcastItem: Codable, Identifiable, Equatable {
    var id: String { "\(sortOrder)-\(content)" }
    let content: String
    let sortOrder: Int

    enum CodingKeys: String, CodingKey {
        case content
        case sortOrder = "sort_order"
    }

    /// 创建语音播报预设。
    init(content: String, sortOrder: Int) {
        self.content = content
        self.sortOrder = sortOrder
    }

    /// 宽松解码语音播报预设。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        content = try container.decodeLossyString(forKey: .content).trimmingCharacters(in: .whitespacesAndNewlines)
        sortOrder = try container.decodeLossyInt(forKey: .sortOrder) ?? 0
    }
}

/// 保存排队设置请求体。
struct ScenicQueueSaveSettingRequest: Encodable, Equatable {
    let scenicId: Int
    let scenicSpotId: Int
    let photoEstimateMin: Int
    let photoEstimateSec: Int
    let firstNoticeThresholdPos: Int
    let firstNoticeSmsEnabled: Int
    let firstNoticeCallEnabled: Int
    let secondNoticeThresholdPos: Int
    let secondNoticeSmsEnabled: Int
    let secondNoticeCallEnabled: Int
    let countdownBroadcastIntervalSec: Int
    let countdownReadableThresholdSec: Int
    let queueDistanceMeter: Int?
    let queueTakeLimit: Int?
    let missCallRequeueOffset: Int?
    let showStartShootButton: Int?
    let autoCallNextCount: Int?
    let businessStartTime: String
    let businessEndTime: String
    let voiceBroadcasts: [ScenicQueueVoiceBroadcastItem]
    let status: Int
    let remark: String?

    enum CodingKeys: String, CodingKey {
        case scenicId = "scenic_id"
        case scenicSpotId = "scenic_spot_id"
        case photoEstimateMin = "photo_estimate_min"
        case photoEstimateSec = "photo_estimate_sec"
        case firstNoticeThresholdPos = "first_notice_threshold_pos"
        case firstNoticeSmsEnabled = "first_notice_sms_enabled"
        case firstNoticeCallEnabled = "first_notice_call_enabled"
        case secondNoticeThresholdPos = "second_notice_threshold_pos"
        case secondNoticeSmsEnabled = "second_notice_sms_enabled"
        case secondNoticeCallEnabled = "second_notice_call_enabled"
        case countdownBroadcastIntervalSec = "countdown_broadcast_interval_sec"
        case countdownReadableThresholdSec = "countdown_readable_threshold_sec"
        case queueDistanceMeter = "queue_distance_meter"
        case queueTakeLimit = "queue_take_limit"
        case missCallRequeueOffset = "miss_call_requeue_offset"
        case showStartShootButton = "show_start_shoot_button"
        case autoCallNextCount = "auto_call_next_count"
        case businessStartTime = "business_start_time"
        case businessEndTime = "business_end_time"
        case voiceBroadcasts = "voice_broadcasts"
        case status
        case remark
    }
}

/// 排队设置变更日志响应。
struct ScenicQueueSettingChangeLogData: Decodable, Equatable {
    let total: Int
    let page: Int
    let pageSize: Int
    let list: [ScenicQueueSettingChangeLogItem]

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

    /// 创建变更日志响应。
    init(total: Int = 0, page: Int = 1, pageSize: Int = 20, list: [ScenicQueueSettingChangeLogItem] = []) {
        self.total = total
        self.page = page
        self.pageSize = pageSize
        self.list = list
    }

    /// 宽松解码变更日志响应。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        total = try container.decodeLossyInt(forKey: .total) ?? 0
        page = try container.decodeLossyInt(forKey: .page) ?? 1
        pageSize = try container.decodeLossyInt(forKey: .pageSize) ?? 20
        list = (try? container.decodeIfPresent([ScenicQueueSettingChangeLogItem].self, forKey: .list)) ?? []
    }
}

/// 排队设置变更日志项。
struct ScenicQueueSettingChangeLogItem: Decodable, Equatable, Identifiable {
    let id: Int64
    let scenicSpotName: String
    let operatorName: String
    let operatorPhoneTail: String
    let summary: String
    let displayText: String
    let createdAt: String

    enum CodingKeys: String, CodingKey {
        case id
        case scenicSpotName = "scenic_spot_name"
        case operatorName = "operator_name"
        case operatorPhoneTail = "operator_phone_tail"
        case summary
        case displayText = "display_text"
        case createdAt = "created_at"
    }

    /// 宽松解码变更日志项。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = Int64(try container.decodeLossyInt(forKey: .id) ?? 0)
        scenicSpotName = try container.decodeLossyString(forKey: .scenicSpotName)
        operatorName = try container.decodeLossyString(forKey: .operatorName)
        operatorPhoneTail = try container.decodeLossyString(forKey: .operatorPhoneTail)
        summary = try container.decodeLossyString(forKey: .summary)
        displayText = try container.decodeLossyString(forKey: .displayText)
        createdAt = try container.decodeLossyString(forKey: .createdAt)
    }
}

/// 排队取号二维码响应。
struct ScenicQueueShootQueueQRCodeData: Decodable, Equatable {
    let qrcodeUrl: String

    enum CodingKeys: String, CodingKey {
        case qrcodeUrl = "qrcode_url"
    }

    /// 创建二维码响应。
    init(qrcodeUrl: String = "") {
        self.qrcodeUrl = qrcodeUrl
    }

    /// 宽松解码二维码响应。
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        qrcodeUrl = try container.decodeLossyString(forKey: .qrcodeUrl)
    }
}

/// 本地排队设置快照，用于服务器无配置或离线时保留关键配置。
struct ScenicQueueSettingsSnapshot: Codable, Equatable {
    var shootMinute: Int = 0
    var shootSecond: Int = 30
    var firstAheadCount: Int = 0
    var firstSms: Bool = false
    var firstPhone: Bool = false
    var secondAheadCount: Int = 0
    var secondSms: Bool = false
    var secondPhone: Bool = false
    var broadcastIntervalSec: Int = 50
    var countdownThresholdSec: Int = 15
    var queueDistanceMeter: Int = 0
    var queueTakeLimit: Int = 0
    var missCallRequeueOffset: Int = 1
    var showStartShootingButton: Bool = true
    var autoCallAheadCount: Int = 0
    var quickCallButtonEnabled: Bool = false
    var prepareCallButtonEnabled: Bool = false
    var businessOpen: Bool = true
    var businessStartTime: String = "10:00"
    var businessEndTime: String = "20:00"

    enum CodingKeys: String, CodingKey {
        case shootMinute = "shoot_minute"
        case shootSecond = "shoot_second"
        case firstAheadCount = "first_ahead_count"
        case firstSms = "first_sms"
        case firstPhone = "first_phone"
        case secondAheadCount = "second_ahead_count"
        case secondSms = "second_sms"
        case secondPhone = "second_phone"
        case broadcastIntervalSec = "broadcast_interval_sec"
        case countdownThresholdSec = "countdown_threshold_sec"
        case queueDistanceMeter = "queue_distance_meter"
        case queueTakeLimit = "queue_take_limit"
        case missCallRequeueOffset = "miss_call_requeue_offset"
        case showStartShootingButton = "show_start_shooting_button"
        case autoCallAheadCount = "auto_call_ahead_count"
        case quickCallButtonEnabled = "quick_call_button_enabled"
        case prepareCallButtonEnabled = "prepare_call_button_enabled"
        case businessOpen = "business_open"
        case businessStartTime = "business_start_time"
        case businessEndTime = "business_end_time"
    }
}

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 ? "1" : "0" }
        return ""
    }

    func decodeLossyInt(forKey key: Key) throws -> Int? {
        if let value = try? decodeIfPresent(Int.self, forKey: key) { return value }
        if let value = try? decodeIfPresent(Double.self, forKey: key) { return Int(value) }
        if let value = try? decodeIfPresent(String.self, forKey: key),
           case let text = value.trimmingCharacters(in: .whitespacesAndNewlines),
           !text.isEmpty {
            return Int(text) ?? Int(Double(text) ?? 0)
        }
        if let value = try? decodeIfPresent(Bool.self, forKey: key) { return value ? 1 : 0 }
        return nil
    }

    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),
           case let text = value.trimmingCharacters(in: .whitespacesAndNewlines).lowercased(),
           !text.isEmpty {
            if ["1", "true", "yes"].contains(text) { return true }
            if ["0", "false", "no"].contains(text) { return false }
        }
        return nil
    }
}
