//
//  ScenicQueueAPIModels.swift
//  suixinkan
//

import Foundation

/// GET /api/app/scenic-queue/queue-stats 响应。
struct ScenicQueueStatsData: Decodable, Equatable {
    let queueCount: Int
    let avgWaitMin: Double
    let time: String?

    enum CodingKeys: String, CodingKey {
        case queueCount = "queue_count"
        case avgWaitMin = "avg_wait_min"
        case time
    }
}

/// GET /api/app/scenic-queue/home 响应。
struct ScenicQueueHomeData: Decodable, Equatable {
    let stats: ScenicQueueHomeStats?
    let list: ScenicQueueHomeListBlock?
    let time: String?
}

/// 排队 home 接口 stats 节点。
struct ScenicQueueHomeStats: Decodable, Equatable {
    let type: Int

    /// 创建 stats 节点；Android DTO 在服务端未返回 type 时默认使用 0。
    init(type: Int = 0) {
        self.type = type
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        type = try container.decodeIfPresent(Int.self, forKey: .type) ?? 0
    }

    private enum CodingKeys: String, CodingKey {
        case type
    }
}

/// 排队 home 接口分页列表节点。
struct ScenicQueueHomeListBlock: Decodable, Equatable {
    let list: [ScenicQueueHomeItem]
    let total: Int
    let page: Int
    let pageSize: Int

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

/// 排队 home 接口单条记录。
struct ScenicQueueHomeItem: Decodable, Equatable {
    let id: Int64
    let queueCode: String
    let mobile: String
    let status: Int
    let statusText: String
    let waitMin: Int
    let aheadCount: Int
    let isCalled: Int
    let createdAt: String?
    let calledAt: String?
    let expiredAt: String?
    let finishedAt: String?
    let identityTag: String?
    let queueBanLabel: String?
    let queueBanDays: Int?
    let queueTime: String?
    let queueCountToday: Int?
    let uid: Int64
    let markAsPhotog: Int
    let markAsFreelancePhotog: Int
    let isMissRequeue: Int
    let isMissRequeueText: String?

    enum CodingKeys: String, CodingKey {
        case id
        case queueCode = "queue_code"
        case mobile
        case status
        case statusText = "status_text"
        case waitMin = "wait_min"
        case aheadCount = "ahead_count"
        case isCalled = "is_called"
        case createdAt = "created_at"
        case calledAt = "called_at"
        case expiredAt = "expired_at"
        case finishedAt = "finished_at"
        case identityTag = "identity_tag"
        case queueBanLabel = "queue_ban_label"
        case queueBanDays = "queue_ban_days"
        case queueTime = "queue_time"
        case queueCountToday = "queue_count_today"
        case uid
        case markAsPhotog = "mark_as_photog"
        case markAsFreelancePhotog = "mark_as_freelance_photog"
        case isMissRequeue = "is_miss_requeue"
        case isMissRequeueText = "is_miss_requeue_text"
    }
}

extension ScenicQueueHomeItem {
    /// 转换为当前排队展示实体。
    func toCurrentTicket() -> ScenicQueueTicket {
        ScenicQueueTicket(
            recordId: id,
            queueNo: queueCode,
            phoneMasked: Self.maskMobile(mobile),
            dialPhoneDigits: mobile.filter(\.isNumber),
            status: isCalled == 1 || statusText.contains("已叫") ? .called : .waiting,
            statusText: statusText.isEmpty ? (isCalled == 1 ? "已叫号" : "待叫号") : statusText,
            waitMinutes: max(0, waitMin),
            peopleAhead: max(0, aheadCount),
            showCompleteAction: isCalled == 1 || statusText.contains("已叫"),
            queueBanLabel: queueBanLabel ?? "",
            identityTag: identityTag ?? "",
            queueTimeDisplay: Self.formatQueueTime(queueTime ?? createdAt),
            queueCountToday: queueCountToday ?? 0,
            uid: uid,
            markAsPhotog: markAsPhotog,
            markAsFreelancePhotog: markAsFreelancePhotog,
            isMissRequeue: isMissRequeue == 1,
            missRequeueText: isMissRequeueText ?? ""
        )
    }

    /// 转换为过号列表展示实体。
    func toSkippedTicket() -> ScenicQueueSkippedTicket {
        ScenicQueueSkippedTicket(
            recordId: id,
            queueNo: queueCode,
            phoneMasked: Self.maskMobile(mobile),
            dialPhoneDigits: mobile.filter(\.isNumber),
            statusText: statusText.isEmpty ? "已过号" : statusText,
            skippedTimeDisplay: Self.formatQueueTime(expiredAt ?? finishedAt ?? calledAt),
            queueBanLabel: queueBanLabel ?? "",
            isMissRequeue: isMissRequeue == 1,
            missRequeueText: isMissRequeueText ?? ""
        )
    }

    private static func maskMobile(_ raw: String) -> String {
        let digits = raw.filter(\.isNumber)
        guard digits.count >= 7 else { return raw }
        let start = digits.prefix(3)
        let end = digits.suffix(4)
        return "\(start)****\(end)"
    }

    private static func formatQueueTime(_ raw: String?) -> String {
        let text = raw?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        guard !text.isEmpty else { return "--" }
        let normalized = text.replacingOccurrences(of: "T", with: " ").replacingOccurrences(of: "Z", with: "")
        let inputFormats = ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"]
        let formatter = DateFormatter()
        formatter.locale = Locale(identifier: "zh_CN")
        for format in inputFormats {
            formatter.dateFormat = format
            if let date = formatter.date(from: normalized) {
                formatter.dateFormat = "MM-dd HH:mm"
                return formatter.string(from: date)
            }
        }
        return String(text.prefix(16))
    }
}

/// POST /api/app/scenic-queue/call 响应 data。
struct ScenicQueueCallData: Decodable, Equatable {
    let id: Int64
    let status: Int
    let statusText: String
    let calledAt: String?

    enum CodingKeys: String, CodingKey {
        case id
        case status
        case statusText = "status_text"
        case calledAt = "called_at"
    }
}

/// POST /api/app/scenic-queue/pass 响应 data。
struct ScenicQueuePassData: Decodable, Equatable {
    let id: Int64
    let status: Int
    let statusText: String

    enum CodingKeys: String, CodingKey {
        case id
        case status
        case statusText = "status_text"
    }
}

/// POST /api/app/scenic-queue/finish 响应 data。
struct ScenicQueueFinishData: Decodable, Equatable {
    let id: Int64
    let status: Int
    let statusText: String

    enum CodingKeys: String, CodingKey {
        case id
        case status
        case statusText = "status_text"
    }
}

/// GET /api/app/scenic-queue/setting 响应。
struct ScenicQueueSettingData: Decodable, Equatable {
    let exists: Bool
    let setting: ScenicQueueSettingItem?
    let time: String?
}

/// 服务端排队设置条目。
struct ScenicQueueSettingItem: Codable, Equatable {
    let id: Int64?
    let scenicId: Int64?
    let scenicSpotId: Int64?
    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 voiceBroadcasts: [ScenicQueueVoiceBroadcastItem]?
    let status: Int?
    let remark: String?
    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 voiceBroadcasts = "voice_broadcasts"
        case status
        case remark
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }
}

extension ScenicQueueSettingItem {
    /// 转换为本地设置快照。
    func toSnapshot(fallback: ScenicQueueSettingsSnapshot = ScenicQueueSettingsSnapshot()) -> ScenicQueueSettingsSnapshot {
        ScenicQueueSettingsSnapshot(
            shootMinute: (photoEstimateMin ?? 0).clamped(to: 0...30),
            shootSecond: (photoEstimateSec ?? 0).clamped(to: 0...59),
            firstAheadCount: (firstNoticeThresholdPos ?? 0).clamped(to: 0...50),
            firstSms: (firstNoticeSmsEnabled ?? 0) == 1,
            firstPhone: (firstNoticeCallEnabled ?? 0) == 1,
            secondAheadCount: (secondNoticeThresholdPos ?? 0).clamped(to: 0...50),
            secondSms: (secondNoticeSmsEnabled ?? 0) == 1,
            secondPhone: (secondNoticeCallEnabled ?? 0) == 1,
            broadcastIntervalSec: ScenicQueueSettingItem.normalizedBroadcastInterval(countdownBroadcastIntervalSec),
            countdownThresholdSec: ScenicQueueSettingItem.normalizedReadableThreshold(countdownReadableThresholdSec),
            queueDistanceMeter: (queueDistanceMeter ?? 0).clamped(to: 0...9_999_990),
            queueTakeLimit: (queueTakeLimit ?? 0).clamped(to: 0...999_999),
            missCallRequeueOffset: (missCallRequeueOffset ?? 1).clamped(to: 1...9_999),
            showStartShootingButton: showStartShootButton.map { $0 == 1 } ?? fallback.showStartShootingButton,
            autoCallAheadCount: (autoCallNextCount ?? fallback.autoCallAheadCount).clamped(to: 0...5),
            quickCallButtonEnabled: fallback.quickCallButtonEnabled,
            prepareCallButtonEnabled: fallback.prepareCallButtonEnabled,
            businessOpen: (status ?? 1) == 1,
            businessStartTime: businessStartTime?.nonEmptyTrimmed ?? "10:00",
            businessEndTime: businessEndTime?.nonEmptyTrimmed ?? "20:00"
        )
    }

    private static func normalizedBroadcastInterval(_ value: Int?) -> Int {
        guard let value, 40...60 ~= value else { return 50 }
        return value
    }

    private static func normalizedReadableThreshold(_ value: Int?) -> Int {
        guard let value, 10...30 ~= value else { return 15 }
        return value
    }
}

/// POST /api/app/scenic-queue/save-setting 请求体。
struct ScenicQueueSaveSettingRequest: Encodable, Equatable {
    let scenicId: Int64
    let scenicSpotId: Int64
    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 ScenicQueueVoiceBroadcastItem: Codable, Equatable {
    let content: String?
    let sortOrder: Int?

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

/// GET /api/app/scenic-queue/shoot-queue-qrcode 响应。
struct ScenicQueueShootQueueQrcodeData: Decodable, Equatable {
    let qrcodeUrl: String?
    let scene: String?
    let page: String?
    let time: String?

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

/// GET /api/app/scenic-queue/setting-change-log 响应。
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
    }
}

/// 配置变更日志条目。
struct ScenicQueueSettingChangeLogItem: Decodable, Equatable, Hashable {
    let id: Int64
    let scenicId: Int?
    let scenicSpotId: Int?
    let scenicSpotName: String
    let operatorId: Int?
    let operatorName: String
    let operatorPhoneTail: String
    let summary: String
    let displayText: String
    let createdAt: String?

    enum CodingKeys: String, CodingKey {
        case id
        case scenicId = "scenic_id"
        case scenicSpotId = "scenic_spot_id"
        case scenicSpotName = "scenic_spot_name"
        case operatorId = "operator_id"
        case operatorName = "operator_name"
        case operatorPhoneTail = "operator_phone_tail"
        case summary
        case displayText = "display_text"
        case createdAt = "created_at"
    }
}

extension ScenicQueueSettingChangeLogItem {
    /// Android 日志卡片的单行元信息：时间、操作人和手机号尾号。
    var metadataLine: String {
        let time: String
        if let createdAt, !createdAt.isEmpty {
            let input = DateFormatter()
            input.locale = Locale(identifier: "en_US_POSIX")
            input.timeZone = TimeZone(identifier: "Asia/Shanghai")
            let formats = ["yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd'T'HH:mm:ssXXXXX", "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX"]
            let parsedDate = formats.lazy.compactMap { format -> Date? in
                input.dateFormat = format
                return input.date(from: createdAt)
            }.first
            if let date = parsedDate {
                let output = DateFormatter()
                output.locale = Locale(identifier: "en_US_POSIX")
                output.timeZone = TimeZone(identifier: "Asia/Shanghai")
                output.dateFormat = "yyyy-MM-dd HH:mm"
                time = output.string(from: date)
            } else {
                time = String(createdAt.replacingOccurrences(of: "T", with: " ").prefix(16))
            }
        } else {
            time = "—"
        }
        let name = operatorName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "未知" : operatorName.trimmingCharacters(in: .whitespacesAndNewlines)
        let tail = operatorPhoneTail.trimmingCharacters(in: .whitespacesAndNewlines)
        return tail.isEmpty ? "\(time) \(name)" : "\(time) \(name) 尾号\(tail)"
    }

    /// 日志卡片主摘要，优先使用 summary，空时回退 displayText。
    var cardSummary: String {
        let value = summary.trimmingCharacters(in: .whitespacesAndNewlines)
        return value.isEmpty ? displayText : value
    }
}

/// 打卡点列表响应。
struct ScenicSpotListResponse: Decodable, Equatable {
    let list: [ScenicSpotItem]
    let total: Int?

    enum CodingKeys: String, CodingKey {
        case list
        case total
    }
}

/// 打卡点列表条目。
struct ScenicSpotItem: Decodable, Equatable {
    let id: Int64
    let name: String
}

/// Socket token 响应。
struct SocketTokenResponse: Decodable, Equatable {
    let socketToken: String

    enum CodingKeys: String, CodingKey {
        case socketToken = "socket_token"
    }
}

private extension Int {
    func clamped(to range: ClosedRange<Int>) -> Int {
        Swift.min(Swift.max(self, range.lowerBound), range.upperBound)
    }
}

private extension String {
    var nonEmptyTrimmed: String? {
        let text = trimmingCharacters(in: .whitespacesAndNewlines)
        return text.isEmpty ? nil : text
    }
}
