package com.market.websocket;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.market.manager.SpotClientSessionManager;
import com.market.model.SpotBaseMessage;
import com.market.model.SpotClientSession;
import com.market.service.SpotMarketPushService;
import com.market.service.SpotMessageHandlerService;
import com.market.service.SpotMessageSenderService;
import com.market.utils.SpotConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 现货行情 WebSocket 处理器
 * <p>
 * 同时兼容旧协议（event=sub/unsub/req）和新协议（op=sub/unsub）。
 * 连接建立后启动消息发送线程和心跳任务。
 *
 * @author system
 * @date 2026-05-07
 */
@Component
public class SpotMarketWebSocketHandler extends TextWebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(SpotMarketWebSocketHandler.class);

    private final SpotClientSessionManager  sessionManager;
    private final SpotMessageHandlerService messageHandlerService;
    private final SpotMessageSenderService  messageSenderService;
    private final SpotMarketPushService     pushService;
    private final ObjectMapper              objectMapper;

    public SpotMarketWebSocketHandler(SpotClientSessionManager sessionManager,
                                      SpotMessageHandlerService messageHandlerService,
                                      SpotMessageSenderService messageSenderService,
                                      SpotMarketPushService pushService) {
        this.sessionManager        = sessionManager;
        this.messageHandlerService = messageHandlerService;
        this.messageSenderService  = messageSenderService;
        this.pushService           = pushService;
        this.objectMapper          = new ObjectMapper();
    }

    /**
     * 连接建立：创建会话，启动发送线程和心跳
     *
     * @param session WebSocket 会话
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        try {
            String clientId = sessionManager.generateClientId();
            SpotClientSession clientSession = new SpotClientSession(clientId, session);

            // 1. 解析 queryString 参数
            Map<String, String> params = parseQueryString(session.getUri() != null
                    ? session.getUri().getQuery() : null);
            clientSession.setParameters(params);

            // 2. 判断是否为 Flutter 客户端
            String clientType = params.getOrDefault(SpotConstant.PARAM_CLIENT_TYPE, "");
            clientSession.setFlutter(SpotConstant.CLIENT_TYPE_FLUTTER.equals(clientType));

            // 3. 提取用户 token/uid（如有）
            String uid = params.get(SpotConstant.PARAM_UID);
            if (uid != null && !uid.isEmpty()) {
                clientSession.setUid(uid);
            }

            // 4. 注册会话
            sessionManager.addSession(clientSession);

            // 5. 启动消息发送线程和心跳
            messageSenderService.startMessageSender(clientSession);
            messageSenderService.startHeartbeat(clientSession);

            log.info("现货 WS 连接建立: clientId={}, flutter={}", clientId, clientSession.isFlutter());
        } catch (Exception e) {
            log.error("现货 WS 连接建立失败: sessionId={}", session.getId(), e);
        }
    }

    /**
     * 接收文本消息并路由到对应处理器
     *
     * @param session WebSocket 会话
     * @param message 文本消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        try {
            String payload = message.getPayload();
            if (payload == null || payload.isBlank()) {
                return;
            }

            // 1. O(1) 反查 SpotClientSession
            SpotClientSession clientSession = sessionManager.getByWsSessionId(session.getId());
            if (clientSession == null) {
                return;
            }

            // 2. 解析 JSON
            JsonNode root = objectMapper.readTree(payload);

            // 3. 判断协议类型并路由
            if (root.has(SpotConstant.FIELD_EVENT)) {
                // 旧协议（event=sub/unsub/req/pong）
                SpotBaseMessage baseMessage = objectMapper.treeToValue(root, SpotBaseMessage.class);
                messageHandlerService.handleMessage(clientSession, baseMessage);
            } else if (root.has(SpotConstant.FIELD_OP)) {
                // 新协议（op=sub/unsub）
                handleNewProtocol(clientSession, root);
            } else if (root.has(SpotConstant.FIELD_PONG)) {
                // 心跳 pong（无 event 字段的简化格式）
                SpotBaseMessage pongMsg = new SpotBaseMessage();
                pongMsg.setPong(root.get(SpotConstant.FIELD_PONG).asLong());
                messageHandlerService.handleMessage(clientSession, pongMsg);
            }

        } catch (Exception e) {
            log.error("现货 WS 消息处理异常: sessionId={}", session.getId(), e);
        }
    }

    /**
     * 连接关闭：停止发送线程、心跳，移除会话及全部订阅
     *
     * @param session WebSocket 会话
     * @param status  关闭状态
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        try {
            SpotClientSession clientSession = sessionManager.getByWsSessionId(session.getId());
            if (clientSession != null) {
                messageSenderService.stopMessageSender(clientSession);
                // 同步清理 pushService 订阅映射
                pushService.removeClient(clientSession.getId());
                sessionManager.removeSession(clientSession.getId());
                log.info("现货 WS 连接关闭: clientId={}, status={}", clientSession.getId(), status);
            }
        } catch (Exception e) {
            log.error("现货 WS 连接关闭处理异常: sessionId={}", session.getId(), e);
        }
    }

    /**
     * 传输错误处理
     *
     * @param session   WebSocket 会话
     * @param exception 异常
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        log.warn("现货 WS 传输异常: sessionId={}, error={}", session.getId(), exception.getMessage());
    }

    // ------------------------------------------------------------------ private

    /**
     * 处理新协议消息（op=sub/unsub），同步更新 pushService 订阅映射
     *
     * @param clientSession 客户端会话
     * @param root          JSON 根节点
     */
    private void handleNewProtocol(SpotClientSession clientSession, JsonNode root) {
        String op = root.path(SpotConstant.FIELD_OP).asText();
        JsonNode argsNode = root.path(SpotConstant.FIELD_ARGS);

        if (!argsNode.isArray()) {
            return;
        }

        if (SpotConstant.EVENT_SUB.equals(op)) {
            // 新协议订阅：args 为 channel 列表
            for (JsonNode arg : argsNode) {
                String channel = arg.asText().toLowerCase();
                SpotClientSession.SubscribeData subscribeData = new SpotClientSession.SubscribeData();
                subscribeData.setAction(SpotConstant.SUBSCRIBE_ACTION);
                subscribeData.setFlutter(clientSession.isFlutter());
                clientSession.getSubscriptions().put(channel, subscribeData);
                // 同步注册到推送服务
                pushService.subscribe(clientSession.getId(), channel);
                pushAndCacheLatest(clientSession, channel);
            }
        } else if (SpotConstant.EVENT_UNSUB.equals(op)) {
            // 新协议取消订阅
            for (JsonNode arg : argsNode) {
                String channel = arg.asText().toLowerCase();
                clientSession.getSubscriptions().remove(channel);
                // 同步从推送服务移除
                pushService.unsubscribe(clientSession.getId(), channel);
            }
        }
    }

    /**
     * 订阅后立即推送最新缓存（若有）
     *
     * @param clientSession 客户端会话
     * @param channel       订阅 channel
     */
    private void pushAndCacheLatest(SpotClientSession clientSession, String channel) {
        Object latest = pushService.getLatest(channel);
        if (latest != null) {
            pushService.pushToSession(clientSession, channel, latest);
        }
    }

    /**
     * 解析 URL queryString 为参数 Map
     *
     * @param queryString URL 查询字符串
     * @return 参数 Map
     */
    private Map<String, String> parseQueryString(String queryString) {
        Map<String, String> params = new HashMap<>();
        if (queryString == null || queryString.isEmpty()) {
            return params;
        }
        for (String pair : queryString.split("&")) {
            int idx = pair.indexOf('=');
            if (idx > 0 && idx < pair.length() - 1) {
                params.put(pair.substring(0, idx), pair.substring(idx + 1));
            } else if (idx > 0) {
                params.put(pair.substring(0, idx), "");
            }
        }
        return params;
    }
}
