package com.njcn.gather.detection.util.socket.websocket;

import com.alibaba.fastjson.JSON;
import com.njcn.gather.detection.lock.DetectionLockManager;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.pojo.vo.WebSocketVO;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * WebSocket会话管理器
 *
 * <p>负责管理电能质量检测系统中的WebSocket连接会话和检测参数，主要功能包括：</p>
 * <ul>
 *   <li>WebSocket连接会话的添加、删除和管理</li>
 *   <li>向指定用户推送实时消息（文本消息和结构化消息）</li>
 *   <li>全局检测参数的存储和管理</li>
 * </ul>
 *
 * <p><b>线程安全性：</b></p>
 * 使用ConcurrentHashMap确保在高并发环境下的线程安全。
 *
 * <p><b>使用场景：</b></p>
 * <ul>
 *   <li>检测进度实时推送</li>
 *   <li>检测结果数据推送</li>
 *   <li>设备状态变更通知</li>
 *   <li>异常信息推送</li>
 * </ul>
 *
 * <p><b>消息推送方式：</b></p>
 * <ul>
 *   <li>{@link #sendMsg(String, String)} - 发送纯文本消息</li>
 *   <li>{@link #sendMessage(String, WebSocketVO)} - 发送结构化JSON消息</li>
 * </ul>
 *
 * @author wr
 * @version 1.0
 * @date 2024/12/11 13:04
 * @see com.njcn.gather.detection.util.socket.websocket.WebSocketHandler WebSocket处理器
 * @see com.njcn.gather.detection.pojo.vo.WebSocketVO WebSocket消息对象
 * @since 检测系统 v2.3.12
 */
@Slf4j
public class WebServiceManager {

    /**
     * WebSocket用户会话存储
     * key: 用户ID, value: WebSocket连接通道
     */
    private static final Map<String, Channel> userSessions = new ConcurrentHashMap<>();

    /**
     * 检测参数存储
     * key: 用户ID(userPageId), value: 检测参数对象
     * 支持多用户并发检测，每个用户的检测参数独立存储
     */
    private static final Map<String, PreDetectionParam> preDetectionParamMap = new ConcurrentHashMap<>();

    /**
     * 添加用户WebSocket会话
     *
     * @param userId  用户ID，不能为null
     * @param channel WebSocket连接通道，不能为null
     */
    public static void addUser(String userId, Channel channel) {
        userSessions.put(userId, channel);
    }


    /**
     * 根据用户ID移除会话（推荐使用）
     * 时间复杂度：O(1)
     *
     * @param userId 用户ID
     * @return 被移除的Channel，如果不存在则返回null
     */
    public static Channel removeByUserId(String userId) {
        return userSessions.remove(userId);
    }

    /**
     * 仅当当前会话仍绑定到指定通道时才移除，避免旧连接误删新连接映射。
     */
    public static boolean removeByUserId(String userId, Channel channel) {
        if (userId == null || channel == null) {
            return false;
        }
        return userSessions.remove(userId, channel);
    }

    /**
     * 根据channelId移除会话（兼容老版本）
     * 时间复杂度：O(n)，建议使用removeByUserId替代
     *
     * @param channelId 通道ID
     * @deprecated 建议使用 {@link #removeByUserId(String)} 替代
     */
    @Deprecated
    public static void removeChannel(String channelId) {
        // 遍历并删除
        Iterator<Map.Entry<String, Channel>> iterator = userSessions.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Channel> entry = iterator.next();
            if (entry.getValue().id().toString().equals(channelId)) {
                iterator.remove();
                break; // 找到后立即退出，避免继续遍历
            }
        }
    }


    /**
     * 发送纯文本消息给指定用户
     *
     * @param userId 目标用户ID
     * @param msg    要发送的文本消息
     */
    public static void sendMsg(String userId, String msg) {
        Channel channel = userSessions.get(userId);
        if (Objects.nonNull(channel) && channel.isActive()) {
            TextWebSocketFrame frame = new TextWebSocketFrame(msg);
            channel.writeAndFlush(frame);
        } else {
            log.error("WebSocket推送消息失败，用户连接已断开，时间: {}, userId: {}", LocalDateTime.now(), userId);
            WebSocketHandler.cleanupSocketResources(userId);
        }
    }

    /**
     * 发送结构化消息给指定用户
     * 消息会被序列化为JSON格式后发送
     *
     * @param userId      目标用户ID
     * @param webSocketVO 要发送的结构化消息对象
     */
    public static void sendMessage(String userId, WebSocketVO<Object> webSocketVO) {
        Channel channel = userSessions.get(userId);
        if (Objects.nonNull(channel) && channel.isActive()) {
            TextWebSocketFrame frame = new TextWebSocketFrame(JSON.toJSONString(webSocketVO));
            channel.writeAndFlush(frame);
        } else {
            log.error("WebSocket推送结构化消息失败，用户连接已断开，时间: {}, userId: {}", LocalDateTime.now(), userId);
            WebSocketHandler.cleanupSocketResources(userId);
        }
    }

    public static void broadcast(String msg) {
        for (Map.Entry<String, Channel> entry : userSessions.entrySet()) {
            String userId = entry.getKey();
            Channel channel = entry.getValue();
            if (Objects.nonNull(channel) && channel.isActive()) {
                channel.writeAndFlush(new TextWebSocketFrame(msg));
            } else {
                log.error("WebSocket broadcast failed, disconnected user, time: {}, userId: {}", LocalDateTime.now(), userId);
                WebSocketHandler.cleanupSocketResources(userId);
            }
        }
    }

    /**
     * 存储检测参数（基于用户ID）
     * 支持多用户并发检测，每个用户的检测参数独立存储
     *
     * @param userId            用户ID（登录名）
     * @param preDetectionParam 检测参数对象
     * @throws IllegalArgumentException 当userId或检测参数为空时抛出
     */
    public static void addPreDetectionParam(String userId, PreDetectionParam preDetectionParam) {
        if (userId == null || userId.trim().isEmpty()) {
            throw new IllegalArgumentException("用户ID不能为空");
        }
        if (preDetectionParam == null) {
            throw new IllegalArgumentException("检测参数不能为空");
        }
        preDetectionParamMap.put(userId, preDetectionParam);
    }


    /**
     * 获取指定用户的检测参数
     *
     * @param userId 用户ID
     * @return 检测参数对象，如果不存在则返回null
     */
    public static PreDetectionParam getPreDetectionParam(String userId) {
        return preDetectionParamMap.get(userId);
    }

    /**
     * 获取当前检测参数（兼容老版本）
     * 注意：该方法已废弃，建议使用 {@link #getPreDetectionParam(String)}
     *
     * @return 检测参数对象，如果不存在则返回null
     * @deprecated 多用户并发场景下该方法不安全，请使用 {@link #getPreDetectionParam(String)}
     */
    @Deprecated
    public static PreDetectionParam getPreDetectionParam() {
        if (preDetectionParamMap.size() == 1) {
            return preDetectionParamMap.values().iterator().next();
        }
        log.warn("存在多个检测参数，无法确定返回哪个，当前参数数量: {}", preDetectionParamMap.size());
        return null;
    }

    /**
     * 移除指定用户的检测参数
     *
     * @param userId 用户ID
     * @return 被移除的检测参数，如果不存在则返回null
     */
    public static PreDetectionParam removePreDetectionParam(String userId) {
        return preDetectionParamMap.remove(userId);
    }

    /**
     * 清空所有检测参数
     */
    public static void removeAllPreDetectionParam() {
        preDetectionParamMap.clear();
    }

    /**
     * 清空所有检测参数（兼容老版本）
     *
     * @deprecated 建议使用 {@link #removeAllPreDetectionParam()} 或 {@link #removePreDetectionParam(String)}
     */
    @Deprecated
    public static void removePreDetectionParam() {
        removeAllPreDetectionParam();
    }

    // ================================ 实用功能方法 ================================

    /**
     * 获取当前在线用户数量
     *
     * @return 在线用户数量
     */
    public static int getOnlineUserCount() {
        return userSessions.size();
    }

    /**
     * 检查指定用户是否在线
     *
     * @param userId 用户ID
     * @return true如果用户在线且连接活跃，否则返回false
     */
    public static boolean isUserOnline(String userId) {
        Channel channel = userSessions.get(userId);
        return Objects.nonNull(channel) && channel.isActive();
    }

    /**
     * 获取所有在线用户ID集合
     *
     * @return 在线用户ID集合的快照
     */
    public static java.util.Set<String> getOnlineUserIds() {
        return new java.util.HashSet<>(userSessions.keySet());
    }

    // ================================ 检测消息推送方法 ================================

    /**
     * 发送检测相关消息给指定用户
     * <p>用于推送检测状态、进度、结果等结构化消息</p>
     *
     * @param userId      目标用户ID
     * @param requestId   请求ID，用于标识消息类型和流程
     * @param operateCode 操作代码，标识具体的操作类型
     * @param data        数据载荷，可以是任意类型的数据
     * @param desc        描述信息
     * @since v2.3.12 重构版本
     */
    public static void sendDetectionMessage(String userId, String requestId, String operateCode, Object data, String desc) {
        WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
        webSocketVO.setRequestId(requestId);
        webSocketVO.setOperateCode(operateCode);
        webSocketVO.setData(data);
        webSocketVO.setDesc(desc);
        sendMessage(userId, webSocketVO);
    }

    /**
     * 发送未知错误消息给指定用户
     * <p>用于处理系统无法识别的操作或未知异常情况</p>
     *
     * @param userId 目标用户ID
     * @since v2.3.12 重构版本
     */
    public static void sendUnknownErrorMessage(String userId) {
        WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
        webSocketVO.setRequestId(SourceOperateCodeEnum.UNKNOWN_OPERATE.getValue());
        webSocketVO.setData(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
        webSocketVO.setOperateCode(SourceOperateCodeEnum.UNKNOWN_OPERATE.getMsg());
        sendMessage(userId, webSocketVO);
    }

    /**
     * 发送检测错误消息给指定用户
     * <p>用于推送特定类型的检测错误信息</p>
     *
     * @param userId    目标用户ID
     * @param errorType 错误类型枚举
     * @since v2.3.12 重构版本
     */
    public static void sendDetectionErrorMessage(String userId, SourceOperateCodeEnum errorType) {
        WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
        webSocketVO.setRequestId(errorType.getValue());
        webSocketVO.setData(errorType.getMsg());
        webSocketVO.setOperateCode(errorType.getValue());
        sendMessage(userId, webSocketVO);
        // 守门员：错误推送即视为本次检测终态，释放检测锁（幂等，与显式释放点叠加双保险）
        releaseLockOnTerminal(userId, errorType);
    }

    /**
     * 发送检测错误消息给指定用户
     * <p>用于推送特定类型的检测错误信息</p>
     *
     * @param userId    目标用户ID
     * @param requestId    请求ID
     * @param errorType 错误类型枚举
     * @since v2.3.12 重构版本
     */
    public static void sendDetectionErrorMessage(String userId, String requestId, SourceOperateCodeEnum errorType) {
        WebSocketVO<Object> webSocketVO = new WebSocketVO<>();
        webSocketVO.setRequestId(requestId);
        webSocketVO.setData(errorType.getMsg());
        webSocketVO.setOperateCode(errorType.getValue());
        sendMessage(userId, webSocketVO);
        // 守门员：理由同上
        releaseLockOnTerminal(userId, errorType);
    }

    /**
     * 守门员释放锁
     * <p>覆盖业务回调里所有走 {@code sendDetectionErrorMessage} 的失败路径，
     * 等价于在 detection/handler 全目录的错误终态点显式释放。与各 Netty handler
     * 内的显式释放幂等叠加，形成双保险。</p>
     *
     * <p>注：业务"正常完成"路径不走此方法（数模式 formalDeal 已在 Phase 1 显式释放；
     * 比对模式正常完成走 sendMsg 推 ERROR_FLOW_END，依赖 WS 断开后心跳超时兜底）。</p>
     */
    private static void releaseLockOnTerminal(String userId, SourceOperateCodeEnum errorType) {
        if (userId == null || userId.isEmpty()) {
            return;
        }
        DetectionLockManager.getInstance()
                .releaseIfMatchPage(userId, "WS_ERROR_PUSH:" + errorType.name());
    }
}
    
