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

import Foundation

/// 排队 WebSocket 消息。
struct ScenicQueueSocketMessage: Equatable {
    static let queueUpdatedAction = "scenic_spot_queue_updated"
    static let ticketCalledAction = "scenic_queue_ticket_called"

    let code: Int
    let data: ScenicQueueSocketData?

    var isScenicQueueEvent: Bool {
        let action = data?.action ?? ""
        return action == Self.queueUpdatedAction || action == Self.ticketCalledAction
    }
}

/// 排队 WebSocket data 块。
struct ScenicQueueSocketData: Equatable {
    let action: String
    let params: ScenicQueueSocketParams?
}

/// 排队 WebSocket 参数。
struct ScenicQueueSocketParams: Equatable {
    let scenicSpotId: Int64?
    let recordId: Int64?
    let operatorUid: Int64?
    let eventId: String?
}

@MainActor
/// 排队 WebSocket 客户端，负责订阅排队更新和叫号事件。
final class ScenicQueueSocketClient {
    private var webSocketTask: URLSessionWebSocketTask?
    private var receiveTask: Task<Void, Never>?

    /// 连接 WebSocket 并订阅指定打卡点的排队事件。
    func connect(
        socketToken: String,
        scenicSpotId: Int,
        onMessage: @escaping @MainActor (ScenicQueueSocketMessage) -> Void
    ) {
        let token = socketToken.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !token.isEmpty else { return }
        disconnect(reason: "Reconnect before scenic queue socket connect")

        let task = URLSession.shared.webSocketTask(with: APIEnvironment.current.webSocketURL)
        webSocketTask = task
        task.resume()

        receiveTask = Task { [weak self] in
            await self?.receiveLoop(task: task, onMessage: onMessage)
        }

        Task {
            for payload in Self.subscriptionPayloads(scenicSpotId: scenicSpotId) {
                try? await task.send(.string(payload))
            }
        }
    }

    /// 断开 WebSocket。
    func disconnect(reason: String = "Scenic queue page stopped") {
        receiveTask?.cancel()
        receiveTask = nil
        webSocketTask?.cancel(with: .normalClosure, reason: reason.data(using: .utf8))
        webSocketTask = nil
    }

    private func receiveLoop(
        task: URLSessionWebSocketTask,
        onMessage: @escaping @MainActor (ScenicQueueSocketMessage) -> Void
    ) async {
        while !Task.isCancelled {
            do {
                let message = try await task.receive()
                let text: String?
                switch message {
                case .string(let value):
                    text = value
                case .data(let data):
                    text = String(data: data, encoding: .utf8)
                @unknown default:
                    text = nil
                }
                guard let text, let parsed = Self.parseMessage(text), parsed.isScenicQueueEvent else { continue }
                onMessage(parsed)
            } catch {
                return
            }
        }
    }

    /// 构造订阅 payload，type 与 Android 保持一致。
    static func subscriptionPayloads(scenicSpotId: Int) -> [String] {
        [
            #"{"type":306,"params":{"scenic_spot_id":\#(scenicSpotId)}}"#,
            #"{"type":307,"params":{"scenic_spot_id":\#(scenicSpotId)}}"#
        ]
    }

    /// 解析排队 WebSocket 原始消息。
    static func parseMessage(_ raw: String) -> ScenicQueueSocketMessage? {
        let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
        guard trimmed.hasPrefix("{"), let data = trimmed.data(using: .utf8) else { return nil }
        guard let root = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil }

        let code = intValue(root["code"]) ?? 0
        var socketData: ScenicQueueSocketData?
        if let dataObject = root["data"] as? [String: Any] {
            let action = stringValue(dataObject["action"])
            var params: ScenicQueueSocketParams?
            if let paramsObject = dataObject["params"] as? [String: Any] {
                params = ScenicQueueSocketParams(
                    scenicSpotId: int64Value(paramsObject["scenic_spot_id"]),
                    recordId: int64Value(paramsObject["record_id"]),
                    operatorUid: int64Value(paramsObject["operator_uid"]),
                    eventId: stringValue(paramsObject["event_id"]).trimmingCharacters(in: .whitespacesAndNewlines).nilIfEmpty
                )
            }
            socketData = ScenicQueueSocketData(action: action, params: params)
        }
        return ScenicQueueSocketMessage(code: code, data: socketData)
    }

    private static func stringValue(_ value: Any?) -> String {
        switch value {
        case let value as String:
            return value
        case let value as NSNumber:
            return value.stringValue
        case let value?:
            return "\(value)"
        case nil:
            return ""
        }
    }

    private static func intValue(_ value: Any?) -> Int? {
        if let value = value as? Int { return value }
        if let value = value as? NSNumber { return value.intValue }
        if let value = value as? String { return Int(value) }
        return nil
    }

    private static func int64Value(_ value: Any?) -> Int64? {
        if let value = value as? Int64 { return value }
        if let value = value as? Int { return Int64(value) }
        if let value = value as? NSNumber { return value.int64Value }
        if let value = value as? String { return Int64(value) }
        return nil
    }
}

private extension String {
    var nilIfEmpty: String? {
        isEmpty ? nil : self
    }
}
