package com.zehong.system.netty.handler;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zehong.common.utils.StringUtils;
import com.zehong.common.utils.bean.BeanUtils;
import com.zehong.common.utils.http.HttpUtils;
import com.zehong.system.domain.MesDeviceDomain;
import com.zehong.system.domain.PalletDeviceBinding;
import com.zehong.system.domain.PalletDeviceUploadHistory;
import com.zehong.system.domain.RobotArmCommand;
import com.zehong.system.service.IPalletDeviceBindingService;
import com.zehong.system.service.IPalletDeviceUploadHistoryService;
import com.zehong.system.service.IRobotArmCommandService;
import com.zehong.system.service.ISysConfigService;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import com.zehong.system.udp.RobotArmMessageParser;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import java.io.*;
import java.util.stream.Collectors;

/**
 * @author lenovo
 * @date 2025/7/31
 * @description TODO
 */
@Component
@Sharable
public class NettyUdpServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {
    private static final Logger log = LoggerFactory.getLogger(NettyUdpServerHandler.class);

    // 日志文件保存目录
    // 写法1：使用双反斜杠
    private static final String LOG_DIR = "D:\\BaiduNetdiskDownload\\udp_message_logs\\";

    // 上传MES地址的key
    public static final String UPLOAD_MES_ADDRESS = "uploadMesAddress";
    // 日期格式器，用于生成文件名和日志时间
    private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyyMMdd");
    private static final SimpleDateFormat TIME_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private volatile long lastActivityTime = System.currentTimeMillis();
    /**
     * 当前正在处理的指令信息
     */
    private final Map<String, CommandExecution> currentCommands = new ConcurrentHashMap<>();

    // 线程安全锁，确保文件写入安全
    private final ReentrantLock fileLock = new ReentrantLock();

    @Resource
    private RobotArmWebSocketHandler robotArmWebSocketHandler; // 注入WebSocket处理器

    @Resource
    private IRobotArmCommandService robotArmCommandService;

    @Resource
    private IPalletDeviceBindingService palletDeviceBindingService;

    @Resource
    private ISysConfigService sysConfigService;

    @Resource
    private IPalletDeviceUploadHistoryService palletDeviceUploadHistoryService;

    /**
     * 接收UDP消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        try {
            // 获取消息内容的字节缓冲区
            ByteBuf content = packet.content();

            // 分配与消息内容长度相同的字节数组
            byte[] bytes = new byte[content.readableBytes()];

            // 将缓冲区内容复制到字节数组
            content.readBytes(bytes);

            // 尝试多种编码方式解码，用于排查问题
            String messageGbk = new String(bytes, "GBK");

            // 日志打印多种编码结果，帮助确定发送端使用的编码
            log.info("收到来自{}的UDP消息：", packet.sender());
            log.info("GBK解码: {}", messageGbk);

            // 根据实际情况选择正确的编码方式
            // 这里假设通过日志观察后确定发送端使用GBK编码
            String correctMessage = messageGbk; // 初始值，根据实际情况修改

            // 检测哪种编码更可能是正确的（简单判断）
            if (containsValidChinese(messageGbk)) {
                correctMessage = messageGbk;
            }

            // 保存消息到文件
            //saveMessageToFile(packet.sender().toString(), correctMessage);

            // 处理消息逻辑
            String response = "服务器已收到UDP消息：" + correctMessage;

            // 记录最后活动时间
            lastActivityTime = System.currentTimeMillis();
            // 判断消息类型并进行相应处理
            if (correctMessage.startsWith("calibration")) {
                // 处理校准消息
                handleCalibrationMessage(ctx, packet, correctMessage);
            } else {
                // 处理普通状态消息（原有逻辑）
                handleNormalMessage(ctx, packet, correctMessage);
            }
//            // 解析消息
//            RobotArmMessageParser.RobotArmStatus status =
//                    RobotArmMessageParser.parseMessage(correctMessage);
//
//            if (status != null) {
//                // 处理状态消息
//                processStatusMessage(status);
//
//                // 检查是否为完全空闲状态
//                if (status.isFullyIdle()) {
//                    handleFullyIdleState();
//                }
//            }
//
//            // 回复客户端
//            byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);
//            ctx.writeAndFlush(new DatagramPacket(
//                    io.netty.buffer.Unpooled.copiedBuffer(responseBytes),
//                    packet.sender()));

        } catch (Exception e) {
            log.error("处理UDP消息异常", e);
            // 出现异常时发送故障状态
            sendStatusToWebSocket("error");
        }
    }
    /**
     * 处理普通状态消息（原有逻辑）
     */
    private void handleNormalMessage(ChannelHandlerContext ctx, DatagramPacket packet, String message) {
        // 回复客户端
        String response = "服务器已收到UDP消息：" + message;
        byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);
        ctx.writeAndFlush(new DatagramPacket(
                io.netty.buffer.Unpooled.copiedBuffer(responseBytes),
                packet.sender()));

        // 解析消息
        RobotArmMessageParser.RobotArmStatus status =
                RobotArmMessageParser.parseMessage(message);

        if (status != null) {
            // 处理状态消息
            processStatusMessage(status);

            // 检查是否为完全空闲状态
            if (status.isFullyIdle()) {
                handleFullyIdleState();
            }
        }
    }

    /**
     * 处理校准消息
     */
    private void handleCalibrationMessage(ChannelHandlerContext ctx, DatagramPacket packet, String message) {
        log.info("收到校准消息: {}", message);

        // 示例：解析校准数据
        // String calibrationData = message.substring("calibration".length()).trim();

        // 示例：回复校准确认
        String response = "校准消息已接收";
        byte[] responseBytes = response.getBytes(StandardCharsets.UTF_8);
        ctx.writeAndFlush(new DatagramPacket(
                io.netty.buffer.Unpooled.copiedBuffer(responseBytes),
                packet.sender()));

        // 例如：更新校准状态、保存校准数据、通知前端等
        // 1 根据信息拿到点位信息 假如说1L是传过来的
        List<PalletDeviceBinding> palletDeviceBindings = initAdAndStatus(message);

        // 2 解析数据上传MES 返回如果失败则记录失败，等下次有时间再上传
        initMesDataAndUpload(palletDeviceBindings);
    }


    /**
     * 初始化MES数据并上传
     */
    private void initMesDataAndUpload(List<PalletDeviceBinding> palletDeviceBindings) {

        List<MesDeviceDomain> mesDeviceDomains = new ArrayList<>();

        for (PalletDeviceBinding palletDeviceBinding : palletDeviceBindings) {
            MesDeviceDomain mesDeviceDomain = new MesDeviceDomain();
            // 主板码
            mesDeviceDomain.setMotherboardCode(palletDeviceBinding.getDeviceCode());
            // 零点AD
            mesDeviceDomain.setZeroAdValue(palletDeviceBinding.getAdjustmentZeroAd());
            // 零点AD状态
            mesDeviceDomain.setZeroStatus(palletDeviceBinding.getZeroStatus());
            // 标定AD
            mesDeviceDomain.setCalibratedAdValue(palletDeviceBinding.getCalibrationAd());
            // 标定AD状态
            mesDeviceDomain.setCalibrationStatus(palletDeviceBinding.getCalibrationAdStatus());
            // 浓度
            mesDeviceDomain.setConcentration(palletDeviceBinding.getConcentration());
            // 实时AD
            mesDeviceDomain.setRealTimeAd(palletDeviceBinding.getRealTimeAd());
            // 实时状态
            mesDeviceDomain.setRealTimeStatus(palletDeviceBinding.getRealTimeStatus());
            mesDeviceDomains.add(mesDeviceDomain);
        }

        String mesUploadAddress = sysConfigService.directSelectConfigByKey(UPLOAD_MES_ADDRESS);
        if(StringUtils.isNotBlank(mesUploadAddress)) {

            String result = HttpUtils.sendPost(mesUploadAddress, JSON.toJSONString(mesDeviceDomains));
            if(StringUtils.isBlank(result)) {
            }
            JSONObject jsonObject = JSON.parseObject(result);
            if(jsonObject.getInteger("code") != 200) {

                String data = jsonObject.getString("data");
                if(StringUtils.isNotBlank(data)) {
                    processPalletDeviceUploadHistory(palletDeviceBindings,data);
                }
            }
        }
    }


    /**
     * 处理托盘设备上传历史数据
     */
    private void processPalletDeviceUploadHistory(List<PalletDeviceBinding> palletDeviceBindings,String data){
        JSONObject jsonObject = JSON.parseObject(data);

        if (!jsonObject.containsKey("failedCodes")) {
            return;
        }

        JSONArray failedCodes = jsonObject.getJSONArray("failedCodes");
        if (failedCodes == null || failedCodes.isEmpty()) {
            return;
        }

        // 使用Set提高查找效率
        Set<String> failedCodeSet = failedCodes.stream()
                .map(Object::toString)
                .collect(Collectors.toSet());

        List<PalletDeviceUploadHistory> palletDeviceUploadHistories = palletDeviceBindings.stream()
                .filter(binding -> failedCodeSet.contains(binding.getDeviceCode()))
                .map(this::convertToUploadHistory)
                .collect(Collectors.toList());

        if (!palletDeviceUploadHistories.isEmpty()) {
            palletDeviceUploadHistoryService.batchInsert(palletDeviceUploadHistories);
        }
    }
    /**
     * 转换对象
     */
    private PalletDeviceUploadHistory convertToUploadHistory(PalletDeviceBinding binding) {
        PalletDeviceUploadHistory history = new PalletDeviceUploadHistory();
        // 基础信息字段
        history.setTrayId(binding.getTrayId());
        history.setDeviceCode(binding.getDeviceCode());
        history.setRow(binding.getRow());
        history.setCol(binding.getCol());
        history.setIndex(binding.getIndex());
        history.setNumber(binding.getNumber());
        history.setBindingTime(binding.getBindingTime());
        history.setUnbindingTime(binding.getUnbindingTime());

        // 状态字段
        history.setStatus(binding.getStatus());

        // 时间记录字段
        history.setRecordYear(binding.getRecordYear());
        history.setRecordMonth(binding.getRecordMonth());
        history.setRecordDate(binding.getRecordDate());
        history.setRecordHour(binding.getRecordHour());
        history.setRecordMinute(binding.getRecordMinute());

        // 写入状态字段
        history.setWriteTimeStatus(binding.getWriteTimeStatus());
        history.setRunTimeStatus(binding.getRunTimeStatus());

        // 调零相关字段
        history.setAdjustmentZeroAd(binding.getAdjustmentZeroAd());
        history.setZeroStatus(binding.getZeroStatus());

        // 标定相关字段 - 注意字段名称不同
        history.setCalibrationAd(binding.getCalibrationAd());
        history.setCalibrationStatus(binding.getCalibrationAdStatus()); // 注意：这里字段名不同

        // 浓度字段
        history.setConcentration(binding.getConcentration());

        // 实时AD相关字段
        history.setRealTimeAd(binding.getRealTimeAd());
        history.setRealTimeAdStatus(binding.getRealTimeStatus()); // 注意：这里字段名不同

        // 托盘编号字段 - 注意字段名称不同
        history.setTrayCode(binding.getfTrayCode()); // 注意：这里字段名不同

        // 创建时间和更新时间（如果需要）
        history.setCreateTime(binding.getCreateTime());
        history.setUpdateTime(binding.getUpdateTime());

        return history;
    }
    /**
     * 根据托盘码查询托盘绑定的设备列
     */
    private List<PalletDeviceBinding> initAdAndStatus(String message) {

        List<PalletDeviceBinding> palletDeviceBindings = palletDeviceBindingService.listByTrayCode("1L");

        // 根据message 处理 零点AD 零点AD状态 标定AD 标定AD状态


        // 批量更新
        palletDeviceBindingService.batchUpdateAdAndStatus(palletDeviceBindings);

        return palletDeviceBindings;
    }

    /**
     * 仅处理已完成的指令
     */
    public void onlyCompleted() {
        // 每次只能有一个指令正在执行
        CommandExecution execution = currentCommands.get("127.0.0.1");
        // 如果指令追踪为空，则可能是 刚启动，再去 查一下有没有正在执行的任务
        if(execution == null) {
            List<RobotArmCommand> robotArmCommands = robotArmCommandService.selectTopRunningRobotArmCommands();
            if(robotArmCommands != null && !robotArmCommands.isEmpty()) {
                // 注册指令跟踪
                execution = new CommandExecution();
                execution.commandId = robotArmCommands.get(0).getRobotArmCommandId();
            }
        }

        if (execution != null) {
            robotArmCommandService.completeCommand(execution.commandId);
            currentCommands.remove("127.0.0.1");
            log.info("指令完成: {}", execution.commandId);
        }
    }

    private void processStatusMessage(RobotArmMessageParser.RobotArmStatus status) {
        // 更新前端状态
        if (status.isBusy()) {
            sendStatusToWebSocket("running");
        } else if (status.isOk()) {
            sendStatusToWebSocket("idle");
        } else {
            sendStatusToWebSocket("error");
        }

        // 处理指令完成
        if (status.isFullyIdle()) {
            // 记录详细状态
            log.debug("机械臂状态: code={}, text={}, position=({},{},{},{})",
                    status.getCode(), status.getText(),
                    status.getX(), status.getY(), status.getZ(), status.getR());

            CommandExecution execution = currentCommands.get("127.0.0.1");
            // 如果指令追踪为空，则可能是 刚启动，再去 查一下有没有正在执行的任务
            if(execution == null) {
                List<RobotArmCommand> robotArmCommands = robotArmCommandService.selectTopRunningRobotArmCommands();
                if(robotArmCommands != null && !robotArmCommands.isEmpty()) {
                    // 注册指令跟踪
                    execution = new CommandExecution();
                    execution.commandId = robotArmCommands.get(0).getRobotArmCommandId();
                }
            }

            if (execution != null) {
                robotArmCommandService.completeCommand(execution.commandId);
                currentCommands.remove("127.0.0.1");
                log.info("指令完成: {}", execution.commandId);
            }
        } else {
            log.info("status.isFullyIdle() = false");
        }

    }
    /**
     * 记录当前执行的指令
     */
    public void registerCommandExecution(Long commandId) {
        CommandExecution execution = new CommandExecution();
        execution.commandId = commandId;
        execution.startTime = System.currentTimeMillis();
        // 每次只能有一个正在执行中的，所以先暂时用使用了127.0.0.1
        currentCommands.put("127.0.0.1", execution);
        log.info("注册指令跟踪: {}", commandId);
    }


    private void handleFullyIdleState() {
        // 处理待执行指令
        robotArmCommandService.processPendingCommands();
    }

    /**
     * 简单判断字符串是否包含有效的中文字符
     */
    private boolean containsValidChinese(String str) {
        if (str == null || str.isEmpty()) {
            return false;
        }
        for (char c : str.toCharArray()) {
            // 中文字符的Unicode范围
            if (c >= 0x4E00 && c <= 0x9FA5) {
                return true;
            }
        }
        return false;
    }

    /**
     * 异常处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("UDP通道异常：{}", cause.getMessage(), cause);
        // UDP无连接，通常不需要关闭通道
    }

    /**
     * 心跳检测
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {
                log.info("UDP服务器超过规定时间未收到数据");

                long now = System.currentTimeMillis();
                long idleDuration = now - lastActivityTime;

                // 如果长时间空闲（>2秒）
                if (idleDuration > 20000) {
                    handleLongIdleState();
                }
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
    private void handleLongIdleState() {
        // 检查是否有超时的指令

        // 如果当前没有执行中的指令，尝试处理待执行指令
        if (currentCommands.isEmpty()) {
            handleFullyIdleState();
        }
    }
    /**
     * 发送状态到WebSocket
     */
    private void sendStatusToWebSocket(String status) {
        if (robotArmWebSocketHandler != null) {
            robotArmWebSocketHandler.broadcastStatus(status);
        } else {
            log.warn("WebSocket处理器未初始化，无法发送状态");
        }
    }

    // 添加指令执行状态
    private static class CommandExecution {
        Long commandId;
        long startTime;
    }


    /**
     * 将消息保存到本地文件
     *
     * @param clientAddress 客户端地址
     * @param message       消息内容
     */
    private void saveMessageToFile(String clientAddress, String message) {
        // 创建日志目录（如果不存在）
        File dir = new File(LOG_DIR);
        if (!dir.exists()) {
            dir.mkdirs();
        }

        // 按日期生成文件名，每天一个文件
        String fileName = LOG_DIR + "udp_log_" + DATE_FORMATTER.format(new Date()) + ".log";
        // 构建日志内容
        String logContent = String.format("[%s] 客户端[%s]：%s%n",
                TIME_FORMATTER.format(new Date()),
                clientAddress,
                message);

        // 使用锁确保多线程写入安全
        fileLock.lock();
        BufferedWriter writer = null;
        try {
            // 以追加模式写入文件
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(fileName, true),
                    StandardCharsets.UTF_8));
            writer.write(logContent);
            writer.flush();
        } catch (IOException e) {
            log.error("保存UDP消息到文件失败", e);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    log.error("关闭文件写入流失败", e);
                }
            }
            fileLock.unlock();
        }
    }
}
