package com.zksy.web.websocket;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zksy.base.domain.EquipmentStatus;
import com.zksy.base.service.EquipmentStatusService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 设备状态 WebSocket 处理器（多路复用版）
 * <p>
 * 支持的消息类型（通过 JSON 中的 type 字段区分）：
 * <ul>
 *   <li><b>subscribe</b> — 批量订阅设备，后端状态变更时主动推送</li>
 *   <li><b>unsubscribe</b> — 取消订阅设备</li>
 *   <li><b>deviceStatus</b> — 单次查询设备状态（兼容旧接口）</li>
 *   <li><b>ping</b> — 心跳检测</li>
 * </ul>
 *
 * <h3>协议示例</h3>
 * <pre>
 * // 订阅
 * → {"type":"subscribe","deviceCodes":["DEV001","DEV002"]}
 * ← {"type":"subscribed","deviceCodes":["DEV001","DEV002"]}
 *
 * // 取消订阅
 * → {"type":"unsubscribe","deviceCodes":["DEV001"]}
 * ← {"type":"unsubscribed","deviceCodes":["DEV001"]}
 *
 * // 心跳
 * → {"type":"ping"}
 * ← {"type":"pong","timestamp":1690000000000}
 *
 * // 单次查询（兼容旧接口）
 * → {"type":"deviceStatus","deviceCode":"DEV001"}
 * ← {"type":"deviceStatus","success":true,"data":{...}}
 *
 * // 设备状态变更推送（服务端主动推送）
 * ← {"type":"deviceStatusPush","data":{...}}
 * </pre>
 *
 * @author zksy
 */
@Slf4j
@Component
public class DeviceStatusWebSocketHandler extends TextWebSocketHandler {

    @Autowired
    private EquipmentStatusService equipmentStatusService;

    @Autowired
    private SubscriptionManager subscriptionManager;

    /**
     * 维护所有在线连接，key 为 sessionId
     * 供 RedisListener 跨线程查找会话
     */
    private static final Map<String, WebSocketSession> SESSION_MAP = new ConcurrentHashMap<>();

    /**
     * 根据 sessionId 获取 WebSocket 会话（供 RedisListener 调用）
     */
    public static WebSocketSession getSession(String sessionId) {
        return SESSION_MAP.get(sessionId);
    }

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String sessionId = session.getId();
        SESSION_MAP.put(sessionId, session);
        log.info("WebSocket 连接建立: sessionId={}, remoteAddress={}", sessionId, session.getRemoteAddress());
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
        String payload = message.getPayload();
        log.debug("收到 WebSocket 消息: sessionId={}, payload={}", session.getId(), payload);

        try {
            JSONObject request = JSONObject.parseObject(payload);
            String type = request.getString("type");

            if (type == null || type.isEmpty()) {
                sendError(session, "消息缺少 type 字段");
                return;
            }

            // === 根据 type 分发到不同的业务处理 ===
            switch (type) {
                //订阅
                case "subscribe":
                    handleSubscribe(session, request);
                    break;
                    //取消订阅
                case "unsubscribe":
                    handleUnsubscribe(session, request);
                    break;
                    //查询设备状态（单次查询）
                case "deviceStatus":
                    handleDeviceStatus(session, request);
                    break;
                    //心跳
                case "ping":
                    handlePing(session);
                    break;
                // 后续新增接口在此添加 case 分支
                default:
                    sendError(session, "不支持的消息类型: " + type);
            }
        } catch (Exception e) {
            log.error("处理 WebSocket 消息异常: sessionId={}", session.getId(), e);
            sendError(session, "消息处理失败: " + e.getMessage());
        }
    }

    /**
     * 处理批量订阅
     */
    private void handleSubscribe(WebSocketSession session, JSONObject request) throws IOException {
        JSONArray deviceCodesArr = request.getJSONArray("deviceCodes");
        if (deviceCodesArr == null || deviceCodesArr.isEmpty()) {
            sendError(session, "缺少 deviceCodes 参数");
            return;
        }

        Set<String> deviceCodes = new HashSet<>();
        for (int i = 0; i < deviceCodesArr.size(); i++) {
            String code = deviceCodesArr.getString(i);
            if (code != null && !code.isEmpty()) {
                deviceCodes.add(code);
            }
        }

        if (deviceCodes.isEmpty()) {
            sendError(session, "deviceCodes 不能为空");
            return;
        }

        Set<String> subscribed = subscriptionManager.subscribe(session.getId(), deviceCodes);

        JSONObject response = new JSONObject();
        response.put("type", "subscribed");
        response.put("deviceCodes", subscribed);
        session.sendMessage(new TextMessage(response.toJSONString()));

        log.info("订阅成功: sessionId={}, 订阅设备={}", session.getId(), subscribed);
    }

    /**
     * 处理取消订阅
     */
    private void handleUnsubscribe(WebSocketSession session, JSONObject request) throws IOException {
        JSONArray deviceCodesArr = request.getJSONArray("deviceCodes");
        if (deviceCodesArr == null || deviceCodesArr.isEmpty()) {
            sendError(session, "缺少 deviceCodes 参数");
            return;
        }

        Set<String> deviceCodes = new HashSet<>();
        for (int i = 0; i < deviceCodesArr.size(); i++) {
            String code = deviceCodesArr.getString(i);
            if (code != null && !code.isEmpty()) {
                deviceCodes.add(code);
            }
        }

        if (deviceCodes.isEmpty()) {
            sendError(session, "deviceCodes 不能为空");
            return;
        }

        Set<String> remaining = subscriptionManager.unsubscribe(session.getId(), deviceCodes);

        JSONObject response = new JSONObject();
        response.put("type", "unsubscribed");
        response.put("deviceCodes", deviceCodes);
        response.put("remaining", remaining);
        session.sendMessage(new TextMessage(response.toJSONString()));

        log.info("取消订阅: sessionId={}, 取消设备={}, 剩余订阅={}", session.getId(), deviceCodes, remaining);
    }

    /**
     * 处理设备状态查询（兼容旧接口）
     */
    private void handleDeviceStatus(WebSocketSession session, JSONObject request) throws IOException {
        String deviceCode = request.getString("deviceCode");
        if (deviceCode == null || deviceCode.isEmpty()) {
            sendResponse(session, "deviceStatus", false, null, "缺少 deviceCode 参数");
            return;
        }

        log.info("查询设备状态: deviceCode={}", deviceCode);
        EquipmentStatus status = equipmentStatusService.getByDeviceCode(deviceCode);

        if (status == null) {
            sendResponse(session, "deviceStatus", false, null, "未找到设备状态: deviceCode=" + deviceCode);
        } else {
            sendResponse(session, "deviceStatus", true, status, null);
        }
    }

    /**
     * 处理心跳
     */
    private void handlePing(WebSocketSession session) throws IOException {
        JSONObject response = new JSONObject();
        response.put("type", "pong");
        response.put("timestamp", System.currentTimeMillis());
        session.sendMessage(new TextMessage(response.toJSONString()));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String sessionId = session.getId();
        SESSION_MAP.remove(sessionId);
        subscriptionManager.removeSession(sessionId);
        log.info("WebSocket 连接关闭: sessionId={}, closeStatus={}", sessionId, status);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        String sessionId = session.getId();
        SESSION_MAP.remove(sessionId);
        subscriptionManager.removeSession(sessionId);
        log.error("WebSocket 传输异常: sessionId={}", sessionId, exception);
    }

    /**
     * 发送成功/失败响应
     */
    private void sendResponse(WebSocketSession session, String type, boolean success, Object data, String msg)
            throws IOException {
        JSONObject response = new JSONObject();
        response.put("type", type);
        response.put("success", success);
        if (data != null) {
            response.put("data", data);
        }
        if (msg != null) {
            response.put("msg", msg);
        }
        session.sendMessage(new TextMessage(response.toJSONString()));
    }

    /**
     * 发送错误响应
     */
    private void sendError(WebSocketSession session, String msg) throws IOException {
        JSONObject response = new JSONObject();
        response.put("type", "error");
        response.put("success", false);
        response.put("msg", msg);
        session.sendMessage(new TextMessage(response.toJSONString()));
    }
}