package com.zehong.system.netty.handler;

import com.zehong.system.domain.RobotArmCommand;
import com.zehong.system.netty.event.ProcessCalibrationResultsEvent;
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.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @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 ReentrantLock fileLock = new ReentrantLock();

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

    @Resource
    private IRobotArmCommandService robotArmCommandService;
    @Resource
    private ApplicationEventPublisher eventPublisher; // 新增事件发布器
    // 命令完成锁，用于确保命令完成时只有一个线程执行
    private final Object completionLock = new Object();
    // 使用原子引用，确保状态变更的原子性
    private final AtomicReference<CommandState> commandState = new AtomicReference<>(CommandState.IDLE);

    // 当前执行的指令ID
    private volatile Long currentCommandId = null;// 记录最近一次收到 fullyIdle 状态的时间（毫秒）

    private volatile long lastFullyIdleTime = 0L;

    private enum CommandState {
        IDLE,           // 空闲
        EXECUTING,      // 执行中
        COMPLETING,     // 正在完成（防止重复）
        ERROR           // 错误
    }


    /**
     * 接收UDP消息
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) {
        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);

            // 记录最后活动时间
            lastActivityTime = System.currentTimeMillis();
            // 判断消息类型并进行相应处理
            if (correctMessage.startsWith("@")) {
                // 处理校准消息
                handleCalibrationMessage(ctx, packet, correctMessage);
            } else {
                // 处理普通状态消息（原有逻辑）
                handleNormalMessage(ctx, packet, correctMessage);
            }
        } 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);
        }
    }

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

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

        // 发布事件后一步处理
        eventPublisher.publishEvent(new ProcessCalibrationResultsEvent(this,message));
    }

    /**
     * 仅处理已完成的指令 - 已修改为使用currentCommandId
     */
    public void onlyCompleted() {
        Long commandId = null;

        // 优先从currentCommandId获取
        if (currentCommandId != null) {
            commandId = currentCommandId;
        } else {
            // 如果currentCommandId为空，从数据库查询正在执行的任务
            List<RobotArmCommand> robotArmCommands = robotArmCommandService.selectTopRunningRobotArmCommands();
            if (robotArmCommands != null && !robotArmCommands.isEmpty()) {
                commandId = robotArmCommands.get(0).getRobotArmCommandId();
                // 如果找到了指令ID，更新currentCommandId
                currentCommandId = commandId;
            }
        }

        if (commandId != null) {
            // 使用状态机完成指令
            completeCommandWithStateMachine(commandId);
        }
    }

    /**
     * 使用状态机完成指令
     */
    private void completeCommandWithStateMachine(Long commandId) {
        // 检查当前状态和指令ID是否匹配
        if (commandId.equals(currentCommandId) &&
                commandState.compareAndSet(CommandState.EXECUTING, CommandState.COMPLETING)) {
            try {
                robotArmCommandService.completeCommand(commandId);
                log.info("指令完成: {}", commandId);

                // 清理状态
                currentCommandId = null;
                commandState.set(CommandState.IDLE);

                // 处理待执行指令
                handleFullyIdleState();
            } catch (Exception e) {
                log.error("指令完成失败: {}", commandId, e);
                commandState.set(CommandState.ERROR);
            }
        } else {
            log.warn("无法完成指令 {}，当前状态: {}, 当前指令ID: {}",
                    commandId, commandState.get(), currentCommandId);
        }
    }

    private void processStatusMessage(RobotArmMessageParser.RobotArmStatus status) {
        // 更新前端状态
        if (status.isBusy()) {
            sendStatusToWebSocket("running");
        } else if (status.isOk()) {
            sendStatusToWebSocket("idle");
        } else {
            sendStatusToWebSocket("error");
        }
        // 处理指令完成逻辑 - 使用状态机防止重复
        if (status.isFullyIdle()) {
            handleCompleteState();
        } else {
            log.info("status.isFullyIdle() = false");
        }
    }

    private void handleCompleteState() {
        // 检查时间条件：必须连续 fullyIdle 超过30秒
        long now = System.currentTimeMillis();
        if (now - lastFullyIdleTime < 30000) {
            log.info("机械臂 fullyIdle 状态持续时间不足30秒，暂不完成指令。已持续 {} 毫秒", now - lastFullyIdleTime);
            return;
        }

        // 只在完全空闲时处理
        Long commandIdToComplete = currentCommandId;

        if (commandIdToComplete != null) {
            synchronized (completionLock) {
                // 再次检查，防止在获取锁期间状态已改变
                if (commandIdToComplete.equals(currentCommandId) &&
                        commandState.compareAndSet(CommandState.EXECUTING, CommandState.COMPLETING)) {
                    try {
                        // 完成指令
                        robotArmCommandService.completeCommand(commandIdToComplete);
                        log.info("指令完成: {}", commandIdToComplete);
                    } catch (Exception e) {
                        log.error("指令完成失败: {}", commandIdToComplete, e);
                    } finally {
                        // 清空当前指令
                        currentCommandId = null;

                        // 状态转为空闲
                        commandState.set(CommandState.IDLE);

                        // 处理待执行指令（这里也加锁确保顺序）
                        synchronized (this) {
                            handleFullyIdleState();
                        }
                    }
                }
            }
        } else {
            // 没有当前指令，直接处理待执行
            synchronized (this) {
                handleFullyIdleState();
            }
        }
    }

    /**
     * 记录当前执行的指令
     */
    public void registerCommandExecution(Long commandId) {
        if (commandState.compareAndSet(CommandState.IDLE, CommandState.EXECUTING)) {
            currentCommandId = commandId;
            lastFullyIdleTime = System.currentTimeMillis();
            log.info("注册指令跟踪: {}", commandId);
        } else {
            throw new IllegalStateException("机械臂当前状态不能执行新指令");
        }
    }


    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;
                if (idleDuration > 20000) {
                    handleLongIdleState();
                }
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }


    private void handleLongIdleState() {
        // 检查当前是否有执行中的指令（使用currentCommandId）
        if (currentCommandId == null) {
            handleFullyIdleState();
        } else {
            log.info("当前有指令正在执行中，指令ID: {}", currentCommandId);
        }
    }
    /**
     * 发送状态到WebSocket
     */
    private void sendStatusToWebSocket(String status) {
        if (robotArmWebSocketHandler != null) {
            robotArmWebSocketHandler.broadcastStatus(status);
        } else {
            log.warn("WebSocket处理器未初始化，无法发送状态");
        }
    }

    /**
     * 将消息保存到本地文件
     *
     * @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();
        }
    }
}
