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

import cn.hutool.core.util.ObjectUtil;
import com.njcn.gather.detection.lock.DetectionLockManager;
import com.njcn.gather.detection.pojo.enums.SourceOperateCodeEnum;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.util.socket.CnSocketUtil;
import com.njcn.gather.detection.util.socket.FormalTestManager;
import com.njcn.gather.device.pojo.enums.PatternEnum;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;

import static com.njcn.gather.detection.util.socket.websocket.WebSocketConstants.*;


/**
 * WebSocket消息处理器
 *
 * <p>负责处理电能质量检测系统中的WebSocket连接和消息通信，主要功能包括：</p>
 * <ul>
 *   <li>WebSocket连接的建立、维护和断开</li>
 *   <li>用户身份验证和会话管理</li>
 *   <li>心跳检测和连接保活</li>
 *   <li>检测状态和结果的实时推送</li>
 *   <li>异常处理和资源清理</li>
 * </ul>
 *
 * <p><b>通信协议：</b></p>
 * <pre>
 * 连接URL: ws://host:port/path?name=userId
 * 心跳消息: "alive" -> "over"
 * 业务消息: JSON格式的检测数据和状态信息
 * </pre>
 *
 * <p><b>安全策略：</b></p>
 * <ul>
 *   <li>连接时必须提供有效的userId参数，否则拒绝连接</li>
 *   <li>支持心跳超时检测，超时3次自动断开连接</li>
 *   <li>连接断开时自动清理相关Socket资源</li>
 * </ul>
 *
 * <p><b>使用场景：</b></p>
 * 主要用于前端实时接收检测进度、检测结果、设备状态等信息的推送，
 * 配合detection模块的ResponseService类实现完整的实时通信链路。
 *
 * @author wr
 * @version 1.0
 * @date 2024/12/10 13:56
 * @see WebServiceManager 会话管理器
 * @see WebSocketConstants 常量定义
 * @see com.njcn.gather.detection.handler.SocketDevResponseService 设备响应处理
 * @see com.njcn.gather.detection.handler.SocketSourceResponseService 源响应处理
 * @since 检测系统 v2.3.12
 */


@Slf4j
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    // ================================ 字段定义 ================================

    /**
     * 心跳超时计数器
     */
    private int times;

    /**
     * 当前WebSocket连接对应的用户ID
     * 在首次HTTP握手时从URL参数中提取并存储
     * 用于后续的Socket连接管理和资源清理
     */
    private String userId;

    /**
     * 心跳响应内容常量
     * 注意：不能预创建TextWebSocketFrame对象，因为ByteBuf状态会改变
     */
    private static final String HEARTBEAT_RESPONSE_TEXT = HEARTBEAT_PONG;

    // ================================ Netty生命周期方法 ================================

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("webSocket服务端通道已建立，channelId: {}", ctx.channel().id());
        super.channelActive(ctx);
    }

    // HTTP握手处理已移至WebSocketPreprocessor，这里只处理WebSocket帧

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        handleWebSocketMessage(ctx, msg);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        log.info("webSocket有新的连接接入，channelId: {}", ctx.channel().id());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        log.info("webSocket客户端退出，channelId: {}, userId: {}", ctx.channel().id(), this.userId);
        if (this.userId != null) {
            WebServiceManager.removeByUserId(this.userId, ctx.channel());
        } else {
            // 备用方案：如果userId为空，使用传统方法
            WebServiceManager.removeChannel(ctx.channel().id().toString());
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("webSocket连接断线，channelId: {}, userId: {}", ctx.channel().id(), this.userId);

        // 确保通道关闭
        if (ctx.channel() != null && ctx.channel().isActive()) {
            ctx.close();
        }

        // 使用Handler实例中保存的userId进行资源清理
        cleanupSocketResources(this.userId);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 处理WebSocket握手完成事件
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            WebSocketServerProtocolHandler.HandshakeComplete handshakeComplete =
                    (WebSocketServerProtocolHandler.HandshakeComplete) evt;

            // 从Channel属性获取userId（由WebSocketPreprocessor设置）
            this.userId = ctx.channel().attr(AttributeKey.<String>valueOf("userId")).get();

            log.info("WebSocket协议升级完成，userId: {}, channelId: {}, requestUri: {}",
                    this.userId, ctx.channel().id(), handshakeComplete.requestUri());

            // 握手完成后建立用户会话
            if (this.userId != null) {
                WebServiceManager.addUser(this.userId, ctx.channel());
                log.info("WebSocket用户会话已建立，userId: {}, channelId: {}", this.userId, ctx.channel().id());
            }

            // 发送连接成功消息给前端
            sendConnectionSuccessMessage(ctx);
            return;
        }

        // 处理心跳超时事件
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            String eventDesc;
            switch (event.state()) {
                case READER_IDLE:
                    eventDesc = "读空闲";
                    log.warn("客户端心跳检测发生超时事件: {}，channelId: {}", eventDesc, ctx.channel().id());
                    times++;
                    if (times > MAX_HEARTBEAT_MISS_COUNT) {
                        log.error("客户端心跳检测空闲次数超过{}次，关闭连接，channelId: {}, userId: {}", MAX_HEARTBEAT_MISS_COUNT, ctx.channel().id(), this.userId);
                        ctx.channel().close();
                        if (this.userId != null) {
                            WebServiceManager.removeByUserId(this.userId, ctx.channel());
                        } else {
                            WebServiceManager.removeChannel(ctx.channel().id().toString());
                        }
                    }
                    break;
                case WRITER_IDLE:
                    log.debug("webSocket写空闲事件，channelId: {}", ctx.channel().id());
                    break;
                case ALL_IDLE:
                    log.debug("webSocket读写空闲事件，channelId: {}", ctx.channel().id());
                    break;
                default:
                    break;
            }
            return;
        }

        // 其他事件传递给父类处理
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        String channelId = ctx.channel().id().toString();
        try {
            // 1. 异常分类记录
            logExceptionByType(channelId, cause);
            // 2. 业务清理
            cleanupOnException(ctx, cause);
            // 3. 连接处理决策
            handleConnectionByExceptionType(ctx, cause);
        } catch (Exception e) {
            // 防止异常处理本身出错
            log.error("异常处理过程中发生错误，强制关闭连接，channelId: {}", channelId, e);
            ctx.close();
        }
    }

    // ================================ HTTP握手处理已移至WebSocketPreprocessor ================================

    // ================================ WebSocket消息处理 ================================

    /**
     * 发送连接成功消息给前端
     * WebSocket握手完成后立即调用，通知前端连接建立成功
     *
     * @param ctx Netty通道上下文
     */
    private void sendConnectionSuccessMessage(ChannelHandlerContext ctx) {
        if (ctx == null || ctx.channel() == null || !ctx.channel().isActive()) {
            log.warn("无法发送连接成功消息：通道不可用, userId: {}", this.userId);
            return;
        }

        try {
            // 构建连接成功消息
            String welcomeMessage = String.format("{\"type\":\"connection\",\"status\":\"success\",\"message\":\"WebSocket连接建立成功\",\"userId\":\"%s\",\"timestamp\":%d}",
                    this.userId, System.currentTimeMillis());

            TextWebSocketFrame frame = new TextWebSocketFrame(welcomeMessage);
            ctx.channel().writeAndFlush(frame);

            log.info("已发送连接成功消息给前端, userId: {}, channelId: {}", this.userId, ctx.channel().id());
        } catch (Exception e) {
            log.error("发送连接成功消息失败, userId: {}, channelId: {}", this.userId, ctx.channel().id(), e);
        }
    }

    /**
     * 处理WebSocket文本消息
     * 这里是所有WebSocket文本消息的统一处理入口
     *
     * @param ctx   Netty通道上下文
     * @param frame WebSocket文本帧
     */
    private void handleWebSocketMessage(ChannelHandlerContext ctx, TextWebSocketFrame frame) {
        String messageText = frame.text();
        // 处理心跳消息
        if (HEARTBEAT_PING.equals(messageText)) {
            handleHeartbeat(ctx);
        } else {
            // 处理业务消息
            handleBusinessMessage(ctx, frame, messageText);
        }
    }

    /**
     * 处理心跳消息
     * 重置超时计数器并回复心跳响应
     *
     * @param ctx Netty通道上下文
     */
    private void handleHeartbeat(ChannelHandlerContext ctx) {
        if (ctx == null || ctx.channel() == null || !ctx.channel().isActive()) {
            log.warn("心跳处理失败：通道不可用，userId: {}", this.userId);
            return;
        }
        log.debug("收到心跳消息，userId: {}, channelId: {}", this.userId, ctx.channel().id());
        // 重置心跳超时计数器
        times = 0;
        // 每次创建新的心跳响应帧，确保内容正确
        TextWebSocketFrame heartbeatFrame = new TextWebSocketFrame(HEARTBEAT_RESPONSE_TEXT);
        ctx.channel().writeAndFlush(heartbeatFrame);
        log.debug("发送心跳响应，userId: {}, channelId: {}", this.userId, ctx.channel().id());
    }

    /**
     * 处理业务消息
     * 可以在这里扩展具体的业务逻辑处理
     *
     * @param ctx         Netty通道上下文
     * @param frame       WebSocket文本帧
     * @param messageText 消息文本内容
     */
    private void handleBusinessMessage(ChannelHandlerContext ctx, TextWebSocketFrame frame, String messageText) {
        log.debug("收到WebSocket业务消息，userId: {}, channelId: {}, message: {}",
                this.userId, ctx.channel().id(), messageText);
        // TODO: 根据业务需要扩展消息处理逻辑
        // 例如：
        // - 解析JSON消息
        // - 根据消息类型分发到不同的处理器
        // - 调用业务服务处理具体逻辑
    }

    // ================================ 异常处理 ================================

    /**
     * 根据异常类型记录不同级别的日志
     */
    private void logExceptionByType(String channelId, Throwable cause) {
        if (cause instanceof IOException) {
            log.info("webSocket网络异常，客户端可能异常断开，channelId: {}, 异常: {}", channelId, cause.getMessage());
        } else if (cause instanceof WebSocketHandshakeException) {
            log.warn("webSocket握手异常，channelId: {}, 异常: {}", channelId, cause.getMessage());
        } else if (cause instanceof DecoderException || cause instanceof CorruptedFrameException) {
            log.error("webSocket协议解码异常，可能是恶意请求，channelId: {}, 异常: {}", channelId, cause.getMessage(), cause);
        } else if (cause instanceof IllegalArgumentException) {
            log.warn("webSocket参数异常，channelId: {}, 异常: {}", channelId, cause.getMessage());
        } else {
            log.error("webSocket未分类异常，channelId: {}, 类型: {}, 异常: {}",
                    channelId, cause.getClass().getSimpleName(), cause.getMessage(), cause);
        }
    }

    /**
     * 异常发生时的业务清理工作
     */
    private void cleanupOnException(ChannelHandlerContext ctx, Throwable cause) {
        if (ctx == null || ctx.channel() == null) {
            log.warn("异常处理：通道上下文为空，无法进行清理");
            return;
        }

        String channelId = ctx.channel().id().toString();

        // 清理会话
        if (this.userId != null) {
            WebServiceManager.removeByUserId(this.userId, ctx.channel());
            log.debug("已清理WebSocket会话，userId: {}, channelId: {}", this.userId, channelId);
        } else {
            WebServiceManager.removeChannel(channelId);
            log.debug("已清理WebSocket会话（使用channelId），channelId: {}", channelId);
        }

        // 清理检测相关资源
        cleanupSocketResources(this.userId);
    }

    /**
     * 根据异常类型决定连接处理策略
     */
    private void handleConnectionByExceptionType(ChannelHandlerContext ctx, Throwable cause) {
        String channelId = ctx.channel().id().toString();
        // URL参数异常但连接本身可能正常，尝试保持连接
        if (cause instanceof IllegalArgumentException &&
                cause.getMessage() != null && cause.getMessage().contains("URL")) {
            log.info("URL参数异常，尝试保持连接，channelId: {}", channelId);
            return;
        }
        // 其他情况都关闭连接
        log.debug("关闭WebSocket连接，channelId: {}", channelId);
        ctx.close();
    }

    // ================================ 资源清理 ================================

    /**
     * 清理Socket相关资源
     *
     * @param userId 用户ID
     */
    public static void cleanupSocketResources(String userId) {
        if (userId == null || userId.trim().isEmpty()) {
            log.warn("userId为空，无法进行Socket连接清理");
            return;
        }

        try {
            PreDetectionParam preDetectionParam = WebServiceManager.getPreDetectionParam(userId);
            if (ObjectUtil.isNotNull(preDetectionParam)) {
                // 使用该用户的检测参数关闭Socket连接
                log.info("使用用户检测参数关闭Socket连接，userId: {}", userId);
                if (FormalTestManager.patternEnum.equals(PatternEnum.CONTRAST)) {
                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
//                    if (FormalTestManager.isRemoveSocket) {
//                        boolean channelActive = SocketManager.isChannelActive(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
//                        if (channelActive) {
//                            SocketManager.removeUser(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
//                        }
//                    } else {
//                        if (FormalTestManager.currentStep == SourceOperateCodeEnum.RECORD_WAVE_STEP1) {
//                            CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
//                        } else if (FormalTestManager.currentStep != SourceOperateCodeEnum.QUITE) {
//                            if (ObjectUtil.isNotNull(FormalTestManager.nonWaveDataSourceEnum)) {
//                                if (FormalTestManager.nonWaveDataSourceEnum == DataSourceEnum.REAL_DATA) {
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
//                                    if (FormalTestManager.statisticsProtocol) {
//                                        CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
//                                    }
//                                } else {
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
//                                }
//                                if (FormalTestManager.isWaveCheck) {
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
//                                }
//                            } else {
//                                if (FormalTestManager.statisticsProtocol) {
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_01, false);
//                                }
//                                if (FormalTestManager.isWaveCheck) {
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_02, false);
//                                    CnSocketUtil.contrastSendquit(preDetectionParam.getUserPageId(), SourceOperateCodeEnum.QUIT_INIT_03, false);
//                                }
//                            }
//                        } else {
//                            SocketManager.removeUser(preDetectionParam.getUserPageId() + CnSocketUtil.CONTRAST_DEV_TAG);
//                        }
//                    }
                } else {
                    CnSocketUtil.quitSendSource(preDetectionParam);
                    CnSocketUtil.quitSend(preDetectionParam);
                }
                // 清理完成后移除该用户的检测参数
                WebServiceManager.removePreDetectionParam(userId);
                // R3 释放：WS 断开 / 客户端关页面 / 心跳超时后顺手释放检测锁
                if (preDetectionParam.getUserPageId() != null) {
                    DetectionLockManager.getInstance()
                            .releaseIfMatchPage(preDetectionParam.getUserPageId(), "WS_DISCONNECTED");
                }
            }
        } catch (Exception e) {
            log.error("清理Socket连接时发生异常，userId: {}", userId, e);
        }
    }

    // ================================ URL解析工具已移至WebSocketPreprocessor ================================
}
