package com.zehong.system.netty.handler;

import com.zehong.system.service.IRobotArmCommandService;
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.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import java.io.*;
import java.util.Date;
/**
 * @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\\";
    // 日期格式器，用于生成文件名和日志时间
    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;

    /**
     * 接收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();

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

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

                // 检查是否为完全空闲状态
                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 processStatusMessage(RobotArmMessageParser.RobotArmStatus status, SocketAddress sender) {
        // 更新前端状态
        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) {
                robotArmCommandService.completeCommand(execution.commandId);
                currentCommands.remove("127.0.0.1");
                log.info("指令完成: {}", execution.commandId);
            }
        }

    }
    /**
     * 记录当前执行的指令
     */
    public void registerCommandExecution(String  address, Long commandId) {
        CommandExecution execution = new CommandExecution();
        execution.commandId = commandId;
        execution.startTime = System.currentTimeMillis();
        currentCommands.put(address, execution);
        log.info("注册指令跟踪: {} -> {}", address, 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();
        }
    }
}
