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

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.njcn.gather.detection.pojo.param.ContrastDetectionParam;
import com.njcn.gather.detection.pojo.param.PreDetectionParam;
import com.njcn.gather.detection.util.socket.cilent.NettyClient;
import com.njcn.gather.detection.util.socket.cilent.NettyContrastClientHandler;
import com.njcn.gather.detection.util.socket.config.SocketConnectionConfig;
import com.njcn.gather.plan.pojo.enums.DataSourceEnum;
import com.njcn.gather.script.pojo.po.SourceIssue;
import io.netty.channel.Channel;
import io.netty.channel.nio.NioEventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Socket连接管理器
 * 提供Socket连接的生命周期管理、消息发送、检测任务管理等功能
 * <p>
 * 包含以下主要功能：
 * 1. 基础连接管理：addUser, removeUser, sendMsg等
 * 2. 智能消息发送：smartSendToSource, smartSendToDevice等（新增）
 * 3. 检测任务管理：targetMap, sourceIssueList等管理
 * 4. 连接状态监控：getConnectionStatus等（新增）
 *
 * @Description: webSocket存储的通道
 * @Author: wr, hongawen
 * @Date: 2024/12/11 13:04
 */
@Slf4j
@Component
public class SocketManager {

    @Resource
    private SocketConnectionConfig socketConnectionConfig;

    /**
     * key为userId（xxx_Source、xxx_Dev），value为channel
     */
    private static final Map<String, Channel> socketSessions = new ConcurrentHashMap<>();

    /**
     * key为userId（xxx_Source、xxx_Dev），value为group
     */
    private static final Map<String, NioEventLoopGroup> socketGroup = new ConcurrentHashMap<>();

    /**
     * 主动关闭中的连接。用于区分业务主动 close 和对端异常断线。
     */
    private static final Map<String, Boolean> activeClosingUsers = new ConcurrentHashMap<>();

    public static void addUser(String userId, Channel channel) {
        socketSessions.put(userId, channel);
    }

    public static void addGroup(String userId, NioEventLoopGroup group) {
        socketGroup.put(userId, group);
    }

    public static void removeUser(String userId) {
        Channel channel = socketSessions.get(userId);
        removeMapping(userId, false);
        if (ObjectUtil.isNotNull(channel)) {
            try {
                markActiveClose(userId);
                channel.close().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(userId + "__" + channel.id() + "关闭了客户端");
        }
    }

    public static void removeMapping(String userId) {
        removeMapping(userId, true);
    }

    private static void removeMapping(String userId, boolean clearActiveClose) {
        socketSessions.remove(userId);
        NioEventLoopGroup eventExecutors = socketGroup.remove(userId);
        if (clearActiveClose) {
            consumeActiveClose(userId);
        }
        if (ObjectUtil.isNotNull(eventExecutors)) {
            eventExecutors.shutdownGracefully();
        }
    }

    public static Channel getChannelByUserId(String userId) {
        return socketSessions.get(userId);
    }

    public static NioEventLoopGroup getGroupByUserId(String userId) {
        return socketGroup.get(userId);
    }

    public static void markActiveClose(String userId) {
        if (StrUtil.isNotBlank(userId)) {
            activeClosingUsers.put(userId, Boolean.TRUE);
        }
    }

    public static boolean consumeActiveClose(String userId) {
        return StrUtil.isNotBlank(userId) && activeClosingUsers.remove(userId) != null;
    }

    public static boolean isActiveClosing(String userId) {
        return StrUtil.isNotBlank(userId) && activeClosingUsers.containsKey(userId);
    }

    public static void sendMsg(String userId, String msg) {
        Channel channel = socketSessions.get(userId);
        if (ObjectUtil.isNotNull(channel)) {
            channel.writeAndFlush(msg + '\n');
            log.info("{}__{}往{}发送数据：{}", userId, channel.id(), channel.remoteAddress(), msg);
        } else {
            log.warn("{}__发送数据：失败通道不存在{}", userId, msg);
        }
    }

    // =================== 智能发送功能 ===================

    /**
     * 智能发送消息到程控源设备
     * 自动从配置文件读取IP和PORT，开发者无需关心网络配置
     * 如果连接不存在且requestId需要建立连接，会自动建立连接后发送
     *
     * @param param 检测参数，包含用户页面ID等信息
     * @param msg   要发送的消息内容（JSON格式，包含requestId字段）
     */
    public void smartSendToSource(PreDetectionParam param, String msg) {
        String requestId = extractRequestId(msg);
        String userId = param.getUserPageId() + CnSocketUtil.SOURCE_TAG;
        // 检查是否需要建立连接
        if (SocketConnectionConfig.needsSourceConnection(requestId)) {
            String ip = socketConnectionConfig.getSource().getIp();
            Integer port = socketConnectionConfig.getSource().getPort();
            // 检查连接是否存在且活跃
            if (!isChannelActive(userId)) {
                log.info("程控源连接不存在，自动建立连接: userId={}, requestId={}", userId, requestId);
                // 异步建立程控源连接并发送消息
                CompletableFuture.runAsync(() -> {
                    NettyClient.connectToSourceStatic(ip, port, param, msg);
                });
                return;
            }
        }

        // 连接已存在或不需要建立连接，直接发送消息
        log.info("直接发送消息到程控源: userId={}, requestId={}", userId, requestId);
        sendMsg(userId, msg);
    }

    /**
     * 智能发送消息到被检设备
     * 自动从配置文件读取IP和PORT，开发者无需关心网络配置
     * 如果连接不存在且requestId需要建立连接，会自动建立连接后发送
     *
     * @param param 检测参数，包含用户页面ID等信息
     * @param msg   要发送的消息内容（JSON格式，包含requestId字段）
     */
    public void smartSendToDevice(PreDetectionParam param, String msg) {
        String requestId = extractRequestId(msg);
        String userId = param.getUserPageId() + CnSocketUtil.DEV_TAG;
        // 检查是否需要建立连接
        if (SocketConnectionConfig.needsDeviceConnection(requestId)) {
            String ip = socketConnectionConfig.getDevice().getIp();
            Integer port = socketConnectionConfig.getDevice().getPort();
            // 检查连接是否存在且活跃
            if (!isChannelActive(userId)) {
                log.info("被检设备连接不存在，自动建立连接: userId={}, requestId={}", userId, requestId);
                // 异步建立被检设备连接并发送消息
                CompletableFuture.runAsync(() -> {
                    NettyClient.connectToDeviceStatic(ip, port, param, msg);
                });
                return;
            }
        }

        // 连接已存在或不需要建立连接，直接发送消息
        log.info("直接发送消息到被检设备: userId={}, requestId={}", userId, requestId);
        sendMsg(userId, msg);
    }


    /**
     * 比对智能发送消息到被检设备
     * 自动从配置文件读取IP和PORT，开发者无需关心网络配置
     * 如果连接不存在且requestId需要建立连接，会自动建立连接后发送
     *
     * @param param 检测参数，包含用户页面ID等信息
     * @param msg   要发送的消息内容（JSON格式，包含requestId字段）
     */
    public void smartSendToContrast(ContrastDetectionParam param, String msg) {
        String requestId = extractRequestId(msg);
        String userId = param.getLoginName() + CnSocketUtil.CONTRAST_DEV_TAG;
        // 检查是否需要建立连接
        if (SocketConnectionConfig.needsDeviceConnection(requestId)) {
            String ip = socketConnectionConfig.getDevice().getIp();
            Integer port = socketConnectionConfig.getDevice().getPort();
            // 检查连接是否存在且活跃
            if (!isChannelActive(userId)) {
                log.info("比对被检设备连接不存在，自动建立连接: userId={}, requestId={}", userId, requestId);
                // 异步建立比对被检设备连接并发送消息
                CompletableFuture.runAsync(() -> {
                    NettyClient.connectToContrastDeviceStatic(ip, port, param, msg);
                });
                return;
            } else {
                PreDetectionParam preDetectionParam = new PreDetectionParam();
                preDetectionParam.setUserPageId(param.getLoginName());
                preDetectionParam.setTestItemList(param.getTestItemList());
                preDetectionParam.setDevIds(param.getDevIds());
                preDetectionParam.setUserId(param.getUserId());
                NettyContrastClientHandler.param = preDetectionParam;
            }
        }

        // 连接已存在或不需要建立连接，直接发送消息
        log.info("直接发送消息到比对被检设备: userId={}, requestId={}", userId, requestId);
        sendMsg(userId, msg);
    }


    // =================== 私有工具方法 ===================

    /**
     * 从消息中提取requestId
     * 支持JSON格式的消息解析
     *
     * @param msg 消息内容
     * @return String requestId，如果解析失败返回"unknown"
     */
    private static String extractRequestId(String msg) {
        try {
            if (StrUtil.isNotBlank(msg)) {
                // 尝试解析JSON格式消息
                JSONObject jsonObject = JSON.parseObject(msg);
                String requestId = jsonObject.getString("requestId");
                if (StrUtil.isNotBlank(requestId)) {
                    return requestId;
                }

                // 如果没有requestId字段，尝试解析request_id字段
                requestId = jsonObject.getString("request_id");
                if (StrUtil.isNotBlank(requestId)) {
                    return requestId;
                }

                // 如果没有JSON字段，尝试从普通字符串中匹配
                if (msg.contains("requestId=")) {
                    String[] parts = msg.split("requestId=");
                    if (parts.length > 1) {
                        String idPart = parts[1].split("[,\\s&]")[0];
                        return idPart.trim();
                    }
                }
            }
        } catch (Exception e) {
            log.warn("解析消息中的requestId失败: msg={}, error={}", msg, e.getMessage());
        }

        return "unknown";
    }

    /**
     * 检查指定用户的Channel是否活跃
     *
     * @param userId 用户ID
     * @return boolean true:连接活跃, false:连接不存在或不活跃
     */
    public static boolean isChannelActive(String userId) {
        Channel channel = getChannelByUserId(userId);
        return ObjectUtil.isNotNull(channel) && channel.isActive();
    }


    /**
     * key:大类型code value:对应小项数量（成功一个后减一）
     */
    private static Map<String, Long> targetMap = new ConcurrentHashMap<>();

    /**
     * 存储所有检测小项
     */
    private static List<SourceIssue> sourceIssueList = new CopyOnWriteArrayList<>();

    /**
     * 用于存储pq_script_checkdata表里Value_Type与树字典表code。key为code,value为id值
     */
    public static Map<String, String> valueTypeMap = new HashMap<>();


    /**
     * 用于存储每个测试小项超时时长key key:检测项 value:时间秒
     */
    public static volatile Map<Integer, Long> clockMap = new ConcurrentHashMap<>();

    /**
     * 用于存储比对式测试时间。
     */
    public static volatile Map<DataSourceEnum, Long> contrastClockMap = new ConcurrentHashMap<>();


    public static void addSourceList(List<SourceIssue> sList) {
        sourceIssueList = sList;
    }

    public static List<SourceIssue> getSourceList() {
        return sourceIssueList;
    }


    public static void delSource(Integer index) {
        sourceIssueList.removeIf(s -> index.equals(s.getIndex()));
    }

    public static void ensureFirst(SourceIssue issue) {
        if (issue == null || issue.getIndex() == null) {
            return;
        }
        if (sourceIssueList.isEmpty()) {
            sourceIssueList.add(0, issue);
            return;
        }
        SourceIssue first = sourceIssueList.get(0);
        if (issue.getIndex().equals(first.getIndex())) {
            return;
        }
        sourceIssueList.removeIf(s -> issue.getIndex().equals(s.getIndex()));
        sourceIssueList.add(0, issue);
    }

    public static void delSourceTarget(String sourceTag) {
        targetMap.remove(sourceTag);
    }


    public static void initMap(Map<String, Long> map) {
        targetMap = map;
    }

    public static void addTargetMap(String scriptType, Long count) {
        targetMap.put(scriptType, count);
    }

    public static Long getSourceTarget(String scriptType) {
        return targetMap.get(scriptType);
    }

    public static void clearFormalRuntime() {
        sourceIssueList.clear();
        targetMap.clear();
        valueTypeMap.clear();
        clockMap.clear();
    }

}
