//
//  ScenicQueueSocketClient.swift
//  suixinkan
//

import Foundation

/// 排队 WebSocket 客户端，订阅打卡点队列刷新与叫号事件。
final class ScenicQueueSocketClient {
    var onMessage: ((ScenicQueueSocketMessage) -> Void)?

    private let environment: APIEnvironment
    private let session: URLSession
    private let decoder = JSONDecoder()
    private var task: URLSessionWebSocketTask?
    private var connectedSpotId: Int64?

    init(environment: APIEnvironment = .current, session: URLSession = .shared) {
        self.environment = environment
        self.session = session
    }

    /// 建立连接并订阅指定打卡点。
    func connect(socketToken: String, scenicId: Int64, scenicSpotId: Int64) {
        guard !socketToken.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
        disconnect()
        connectedSpotId = scenicSpotId
        let webSocketTask = session.webSocketTask(with: environment.webSocketURL)
        task = webSocketTask
        webSocketTask.resume()
        sendSubscribePayloads(scenicSpotId: scenicSpotId)
        receiveLoop()
    }

    /// 断开连接。
    func disconnect() {
        task?.cancel(with: .normalClosure, reason: nil)
        task = nil
        connectedSpotId = nil
    }

    private func sendSubscribePayloads(scenicSpotId: Int64) {
        [
            #"{"type":306,"params":{"scenic_spot_id":\#(scenicSpotId)}}"#,
            #"{"type":307,"params":{"scenic_spot_id":\#(scenicSpotId)}}"#,
        ].forEach { payload in
            task?.send(.string(payload)) { _ in }
        }
    }

    private func receiveLoop() {
        task?.receive { [weak self] result in
            guard let self else { return }
            switch result {
            case .success(let message):
                if case .string(let text) = message {
                    self.handle(text)
                }
                self.receiveLoop()
            case .failure:
                self.task = nil
            }
        }
    }

    private func handle(_ text: String) {
        let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
        guard trimmed.hasPrefix("{"), let data = trimmed.data(using: .utf8) else { return }
        guard let message = try? decoder.decode(ScenicQueueSocketMessage.self, from: data) else { return }
        guard message.isQueueEvent else { return }
        onMessage?(message)
    }
}
