package com.zehong.system.service.impl;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.List;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.common.constant.Constants;
import com.zehong.common.core.domain.AjaxResult;
import com.zehong.common.core.redis.RedisCache;
import com.zehong.common.utils.DateUtils;
import com.zehong.common.utils.StringUtils;
import com.zehong.system.domain.*;
import com.zehong.system.mapper.*;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.netty.handler.NettyUdpServerHandler;
import com.zehong.system.service.IConveyorBeltIpMaintainService;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import com.zehong.system.task.CheckPowerOnCommandEvent;
import com.zehong.system.task.PowerOffCommandEvent;
import com.zehong.system.udp.UdpCommandSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import com.zehong.system.service.IRobotArmCommandService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;

/**
 * 机械臂指令Service业务层处理
 *
 * @author zehong
 * @date 2025-08-04
 */
@Service
public class RobotArmCommandServiceImpl implements IRobotArmCommandService
{
    private static final Logger log = LoggerFactory.getLogger(RobotArmCommandServiceImpl.class);

    @Resource
    private NettyUdpServerHandler nettyUdpServerHandler;

    @Resource
    private RobotArmWebSocketHandler robotArmWebSocketHandler;

    @Resource
    private RobotArmCommandMapper robotArmCommandMapper;

    @Resource
    private TStoreyInfoMapper storeyInfoMapper;
    @Resource
    private TEquipmentInfoMapper equipmentInfoMapper;;

    @Resource
    private TTrayInfoMapper tTrayInfoMapper;

    @Resource
    private UdpCommandSender udpCommandSender;
    @Value("${robot.arm.udp.ip}")
    private String robotArmAddress;

    @Value("${robot.arm.udp.port}")
    private int robotArmPort;
    @Resource
    private ApplicationEventPublisher eventPublisher; // 新增事件发布器

    @Resource
    private PalletDeviceBindingMapper palletDeviceBindingMapper;

    @Resource
    private IConveyorBeltIpMaintainService iConveyorBeltIpMaintainService;

    @Resource
    private RedisCache redisCache;
    private SocketAddress getRobotAddress() {
        try {
            return new InetSocketAddress(
                    InetAddress.getByName(robotArmAddress), robotArmPort);
        } catch (UnknownHostException e) {
            throw new RuntimeException("无效的机械臂地址: " + robotArmAddress);
        }
    }

    /**
     * 机械臂空闲状态下 需要执行的待执行的指令
     */

    @Override
    @Transactional
    public void processPendingCommands() {

        Object cacheObject = redisCache.getCacheObject("sys_config:robotArmPriority");
        String priority = "loading";
        if (cacheObject != null) {
            priority = (String) cacheObject;
        }
        // 合并查询：同时获取上料、下料各1条待执行指令
        List<RobotArmCommand> pendingCommands = robotArmCommandMapper.selectPendingLoadUnloadCommands();

        String feedConveyorIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.FEED_CONVEYOR_IP_AND_PORT);
        String feedIp ;
        int feedPort ;
        String outLetBeltIpAndPort = iConveyorBeltIpMaintainService.selectConfigByKey(Constants.OUT_LET_BELT_IP_AND_PORT);
        String outLetBeltIp;
        int outLetBeltPort;
        // 区分上料、下料指令（根据f_type）
        RobotArmCommand loadingCommand = null;
        RobotArmCommand unloadingCommand = null;
        for (RobotArmCommand command : pendingCommands) {
            if ("0".equals(command.getType())) { // 上料指令（type=0）
                loadingCommand = command;
            } else if ("1".equals(command.getType())) { // 下料指令（type=1）
                unloadingCommand = command;
            }
        }
        if(StringUtils.isNotBlank(feedConveyorIpAndPort) && StringUtils.isNotBlank(outLetBeltIpAndPort)) {

            String[] splitFeed = feedConveyorIpAndPort.split(":");
            feedIp = splitFeed[0];
            feedPort = Integer.parseInt(splitFeed[1]);

            String[] splitOut = outLetBeltIpAndPort.split(":");
            outLetBeltIp = splitOut[0];
            outLetBeltPort = Integer.parseInt(splitOut[1]);

            if((loadingCommand != null && "loading".equals(priority)) ||
                    (unloadingCommand == null && loadingCommand != null && "unloading".equals(priority))) {
                // 传送带检测先去掉
                boolean[] roboticArmEntryConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(feedIp,feedPort);

                log.info("机械臂入口 conveyor 0状态: " + roboticArmEntryConveyorData[0]);
                log.info("机械臂入口 conveyor 1状态: " + roboticArmEntryConveyorData[1]);
                if(roboticArmEntryConveyorData[1]) {
                    sendCommand(loadingCommand, "LOAD");
                }
            } else if(unloadingCommand != null) {
                boolean[] roboticArmExitConveyorData = Modbus4jUtils.getRoboticArmExitConveyorData(outLetBeltIp,outLetBeltPort);
                log.info("机械臂出口 conveyor 0状态: " + roboticArmExitConveyorData[0]);
                log.info("机械臂出口 conveyor 1状态: " + roboticArmExitConveyorData[1]);
                if(!roboticArmExitConveyorData[0]) {
                    log.info("开始处理下料指令: {}", unloadingCommand);
                    sendCommand(unloadingCommand, "UNLOAD");
                }
            }
        }
    }


    private void  sendCommand(RobotArmCommand command, String commandType) {
        // 更新状态为执行中
        command.setStatus("2");
        command.setStartExecutionTime(new Date());
        robotArmCommandMapper.updateRobotArmCommand(command);

        // 如果是下料，则 先断电再去下料
        String storeyCode = command.getStoreyCode();
        if("UNLOAD".equals(commandType)) {
            if(StringUtils.isNotBlank(storeyCode)) {
                String[] split = storeyCode.split("-");

                TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(split[0]);

                int layer = Integer.parseInt(split[1]);

                int registerOffsetInt = layer - 1;

                try {
                    ModbusMaster master = Modbus4jUtils.createModbusMaster(tEquipmentInfo.getfPowerOutageIp(), tEquipmentInfo.getfPowerOutagePort());
                    Modbus4jUtils.writeCoil(master, 1, registerOffsetInt, false);
                    log.info("已发送断电指令 - 设备:{} 层:{}", tEquipmentInfo.getfEquipmentCode(), layer);
                    master.destroy();
                } catch (ModbusTransportException | ModbusInitException e) {
                    log.info("下料发送断电指令失败 - 设备:{} 层:{}", tEquipmentInfo.getfEquipmentCode(), layer);
                    throw new RuntimeException(e);
                }
            }
            // 如果是上料，则 先把 t_story_info 设置为 运行中，占上位置，避免此时 再上料 是使用重复位置
        } else {
            TStoreyInfo tStoreyInfo = new TStoreyInfo();
            tStoreyInfo.setfStoreyCode(command.getStoreyCode());
            tStoreyInfo.setfStatus("1");
            storeyInfoMapper.updateStatusByCode(tStoreyInfo);
        }

        // 通过WebSocket广播更新
        robotArmWebSocketHandler.broadcastCommandUpdate();

        // 发送UDP指令
        SocketAddress address = getRobotAddress();

        udpCommandSender.sendCommandWithId(
                address,
                command.getCommand()
        );

        // 注册指令跟踪
        nettyUdpServerHandler.registerCommandExecution(command.getRobotArmCommandId());
    }

    /**
     * 发送归位指令
     */
    @Override
    public void sendHomeCommand() {
        // 发送UDP指令
        SocketAddress address = getRobotAddress();
        udpCommandSender.sendCommandWithId(
                address,
                "HOME"
        );
        initializationTrayAndStoreyInfo();
    }

    @Override
    public void sendStopCommand() {

        // 发送UDP指令
        SocketAddress address = getRobotAddress();
        udpCommandSender.sendCommandWithId(
                address,
                "STOP"
        );
        initializationTrayAndStoreyInfo();
    }

    /**
     * 回零 或 停止 时 初始化托盘和层信息
     */
    private void initializationTrayAndStoreyInfo() {
        List<RobotArmCommand> robotArmCommands = robotArmCommandMapper.selectTopRunningRobotArmCommands();

        if(robotArmCommands.size() > 0) {

            RobotArmCommand robotArmCommand = robotArmCommands.get(0);
            robotArmCommand.setStatus("4");
            robotArmCommand.setEndExecutionTime(new Date());
            robotArmCommandMapper.updateRobotArmCommand(robotArmCommand);

            String trayCode = robotArmCommand.getTrayCode();
            String storeyCode = robotArmCommand.getStoreyCode();

            // 如果是上料的话
            TStoreyInfo tStoreyInfo = new TStoreyInfo();
            if("0".equals(robotArmCommand.getType())) {
                tStoreyInfo.setfStoreyCode(storeyCode);
                tStoreyInfo.setfStatus("0");
                tStoreyInfo.setUpdateTime(new Date());
                storeyInfoMapper.updateStatusByCode(tStoreyInfo);
            // 如果是下料的话 得把 下料的 流程走完
            } else {
                storeyInfoMapper.unbindByCode(storeyCode);

                // 清理 托盘 和 层的关联关系
                tTrayInfoMapper.clearStoreyCodeByStoreyCode(storeyCode);
            }
        }
    }

    /**
     * 发送指令
     * @param command c
     */
    @Override
    public void sendCommand(String command) {
        SocketAddress robotAddress = getRobotAddress();
        udpCommandSender.sendCommandWithId(
                robotAddress,
                command
        );
    }

    /**
     * 生成下料指令
     * @param command c
     */
    @Override
    public void generateBlankingCommand(String trayCode,String storeyCode,String command) {
        // 创建机械臂任务
        RobotArmCommand robotArmCommand = new RobotArmCommand();
        robotArmCommand.setType("1");
        robotArmCommand.setStatus("1");
        robotArmCommand.setTrayCode(trayCode);
        robotArmCommand.setStoreyCode(storeyCode);
        robotArmCommand.setCreateTime(new Date());
        robotArmCommand.setCommand(command);
        robotArmCommandMapper.insertRobotArmCommand(robotArmCommand);
        notifyCommandsUpdate();
    }

    @Override
    public int findNotCompletedCommand(String command) {
        RobotArmCommand notCompletedCommand = robotArmCommandMapper.findNotCompletedCommand(command);
        if(notCompletedCommand != null) {
            return 1;
        }
        return 0;
    }

    /**
     * 完成指令
     * @param commandId c
     */
    @Override
    @Transactional
    public void completeCommand(Long commandId) {
        log.info("开始处理完成指令: {}", commandId);
        RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
        if (command != null && "2".equals(command.getStatus())) {

            log.info("command != null && \"2\".equals(command.getStatus()");
            // 发送上电指令
            try {
                String storeyCode = command.getStoreyCode();
                String equitmentCode;
                int registerOffset;
                if(storeyCode.contains("-")) {
                    log.info("storeyCode.contains(\"-\")");
                    String[] parts = storeyCode.split("-");
                    equitmentCode = parts[0];
                    registerOffset = Integer.parseInt(parts[1]);
                    TEquipmentInfo tEquipmentInfo = equipmentInfoMapper.selectTEquipmentInfoByCode(equitmentCode);

                    if(tEquipmentInfo != null) {
                        log.info("tEquipmentInfo != null");
                        String powerOutageIp = tEquipmentInfo.getfPowerOutageIp();
                        Integer powerOutagePort = tEquipmentInfo.getfPowerOutagePort();
                        log.info("powerOutageIp: {}, powerOutagePort: {},command.getType:{}", powerOutageIp, powerOutagePort, command.getType());
                        if(StringUtils.isNotBlank(powerOutageIp) && powerOutagePort != null) {
                            if("0".equals(command.getType())) {
                                eventPublisher.publishEvent(new CheckPowerOnCommandEvent(
                                        this,
                                        equitmentCode,
                                        powerOutageIp,
                                        powerOutagePort,
                                        registerOffset,
                                        registerOffset - 1
                                ));
                            } else {
                                eventPublisher.publishEvent(new PowerOffCommandEvent(
                                        this,
                                        equitmentCode,
                                        powerOutageIp,
                                        powerOutagePort,
                                        registerOffset,
                                        registerOffset - 1
                                ));
                            }
                        }
                    }
                }
                command.setStatus("4");
                log.info("指令执行成功，状态设置为4");
            } catch (Exception e) {
                command.setStatus("3");
                log.info("执行指令时发生异常: " + e.getMessage());
            }

            command.setEndExecutionTime(new Date());
            robotArmCommandMapper.updateRobotArmCommand(command);

            // 通过WebSocket广播更新
            robotArmWebSocketHandler.broadcastCommandUpdate();
        }
    }

    @Override
    @Transactional
    public void markCommandTimeout(Long commandId) {
        RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
        if (command != null && "2".equals(command.getStatus())) {
            command.setStatus("5"); // 执行异常；先保留此参数
            robotArmCommandMapper.updateRobotArmCommand(command);

            // 通过WebSocket广播更新
            robotArmWebSocketHandler.broadcastCommandUpdate();

            robotArmWebSocketHandler.broadcastStatus("idle");
        }
    }

    /**
     * 查询机械臂指令
     *
     * @param robotArmCommandId 机械臂指令ID
     * @return 机械臂指令
     */
    @Override
    public RobotArmCommand selectRobotArmCommandById(Long robotArmCommandId)
    {
        return robotArmCommandMapper.selectRobotArmCommandById(robotArmCommandId);
    }

    /**
     * 查询机械臂指令列表
     *
     * @param robotArmCommand 机械臂指令
     * @return 机械臂指令
     */
    @Override
    public List<RobotArmCommand> selectRobotArmCommandList(RobotArmCommand robotArmCommand)
    {
        return robotArmCommandMapper.selectRobotArmCommandList(robotArmCommand);
    }

    @Override
    public List<RobotArmCommand> selectTopRunningRobotArmCommands() {
        return robotArmCommandMapper.selectTopRunningRobotArmCommands();
    }

    @Override
    public List<RobotArmCommand> findByType(String type) {
        return robotArmCommandMapper.findByType( type);
    }

    /**
     * 批量新增空闲指令
     *
     * @param robotArmCommand  r
     * @return r
     */
    @Override
    public int insertBlankingRobotArmCommand(RobotArmCommand robotArmCommand) {

        if(robotArmCommand.getTrayCode() ==  null || robotArmCommand.getStoreyCode() == null) {
            throw new RuntimeException("托盘和层不能为空");
        }

        // type 1  是 待下料的
        RobotArmCommand executingCommandByType = robotArmCommandMapper.findExecutingCommandByType(robotArmCommand.getTrayCode(), robotArmCommand.getStoreyCode(), "1");
        if(executingCommandByType != null) {
            throw new RuntimeException("当前托盘正在执行指令");
        }

        TStoreyInfo tStoreyInfo = storeyInfoMapper.selectNearestFreeStorey();
        robotArmCommand.setCommand(tStoreyInfo.getBlankingCommand());
        robotArmCommand.setCreateTime(DateUtils.getNowDate());

        return robotArmCommandMapper.insertRobotArmCommand(robotArmCommand);
    }

    @Override
    @Transactional
    public int addManualCommand(RobotArmCommand robotArmCommand) {

        robotArmCommand.setCreateTime(DateUtils.getNowDate());

        if(StringUtils.isBlank(robotArmCommand.getTrayCode()) || StringUtils.isBlank(robotArmCommand.getStoreyCode())) {
            throw new RuntimeException("托盘编号和层编号不能为空");
        }

        TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByCode(robotArmCommand.getTrayCode());

        if(tTrayInfo == null) {
            throw new RuntimeException("托盘不存在");
        }

        // 看看 此托盘绑定了数据了吗？
        int i1 = palletDeviceBindingMapper.countByTrayId(tTrayInfo.getfTrayId());
        if(i1 == 0) {
            throw new RuntimeException("托盘未绑定设备");
        }

        // 20251104 上料 不需要看托盘状态了
        // 20251210 上料 又需要看托盘状态了
        if(!"4".equals(tTrayInfo.getfStatus())) {
            throw new RuntimeException("托盘状态异常，请联系管理员");
        }

        TStoreyInfo tStoreyInfo = storeyInfoMapper.selectTStoreyInfoByCode(robotArmCommand.getStoreyCode());
        if(tStoreyInfo == null) {
            throw new RuntimeException("层不存在");
        }

        if(!"0".equals(tStoreyInfo.getfStatus())) {
            throw new RuntimeException("层状态异常，请联系管理员");
        }

        robotArmCommand.setStatus("1");
        robotArmCommand.setStoreyCode(tStoreyInfo.getfStoreyCode());
        robotArmCommand.setCommand(tStoreyInfo.getFeedingCommand());
        // 上料 类型
        robotArmCommand.setType("0");
        tTrayInfo.setfStoreyCode(tStoreyInfo.getfStoreyCode());
        tTrayInfo.setfBindingTime(new Date());
        tTrayInfoMapper.updateTTrayInfo(tTrayInfo);

        tStoreyInfo.setfStatus("1");
        storeyInfoMapper.updateStatusByCode(tStoreyInfo);

        // 20260108 新加的把绑定层编号设置到实时数据上
        palletDeviceBindingMapper.updateStoreCodeByTrayId(tTrayInfo.getfTrayId(), tStoreyInfo.getfStoreyCode());

        int i = robotArmCommandMapper.insertRobotArmCommand(robotArmCommand);
        notifyCommandsUpdate();
        return i;
    }

    /**
     * 新增机械臂指令
     *
     * @param robotArmCommand 机械臂指令
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insertRobotArmCommand(RobotArmCommand robotArmCommand)
    {
        robotArmCommand.setCreateTime(DateUtils.getNowDate());

        if(StringUtils.isBlank(robotArmCommand.getTrayCode())) {
            throw new RuntimeException("托盘编号不能为空");
        }

        TTrayInfo tTrayInfo = tTrayInfoMapper.selectTTrayInfoByCode(robotArmCommand.getTrayCode());

        if(tTrayInfo == null) {
            throw new RuntimeException("托盘不存在");
        }

        // 看看 此托盘绑定了数据了吗？
        int i1 = palletDeviceBindingMapper.countByTrayId(tTrayInfo.getfTrayId());
        if(i1 == 0) {
            throw new RuntimeException("托盘未绑定设备");
        }

        // 20251104 上料 不需要看托盘状态了
        // 20251210 上料 又需要看托盘状态了
        if(!"4".equals(tTrayInfo.getfStatus())) {
            throw new RuntimeException("托盘状态异常，请联系管理员");
        }

        TStoreyInfo tStoreyInfo = storeyInfoMapper.selectNearestFreeStorey();
        if(tStoreyInfo != null) {
            robotArmCommand.setStatus("1");
            robotArmCommand.setStoreyCode(tStoreyInfo.getfStoreyCode());
            robotArmCommand.setCommand(tStoreyInfo.getFeedingCommand());
            tTrayInfo.setfStoreyCode(tStoreyInfo.getfStoreyCode());
            tTrayInfo.setfBindingTime(new Date());
            tTrayInfoMapper.updateTTrayInfo(tTrayInfo);

            tStoreyInfo.setfStatus("1");
            storeyInfoMapper.updateStatusByCode(tStoreyInfo);

            // 20260108 新加的把绑定层编号设置到实时数据上
            palletDeviceBindingMapper.updateStoreCodeByTrayId(tTrayInfo.getfTrayId(), tStoreyInfo.getfStoreyCode());

        } else {
            robotArmCommand.setStoreyCode("待分配位置");
        }

        int i = robotArmCommandMapper.insertRobotArmCommand(robotArmCommand);
        notifyCommandsUpdate();
        return i;
    }

    /**
     * 修改机械臂指令
     *
     * @param commandId 机械臂指令
     * @return 结果
     */
    @Override
    public AjaxResult sureCompletedCommand(Long commandId) {

        if(commandId == null) {
            return AjaxResult.error("参数错误");
        }
        RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
        if (command == null) {
            return AjaxResult.error("指令不存在");
        }
        if(!"2".equals(command.getStatus())) {
            return AjaxResult.error("只有待执行状态的指令才能确认完成");
        }
        try {
            nettyUdpServerHandler.onlyCompleted();
        } catch (Exception e) {
            return AjaxResult.error("执行确认完成指令时发生异常: " + e.getMessage());
        }

        return AjaxResult.success();
    }

    @Override
    @Transactional
    public void powerOnCommand(Long commandId) {
        RobotArmCommand command = robotArmCommandMapper.selectRobotArmCommandById(commandId);
        if (command == null) {
            throw new RuntimeException("指令不存在");
        }

        if (!"3".equals(command.getStatus())) {
            throw new RuntimeException("只有未上电状态的指令才能执行上电操作");
        }

        // 上完电 此条指令就结束了
        command.setStatus("4");
        robotArmCommandMapper.updateRobotArmCommand(command);

        // 发送上电指令给机械臂
        String udpMessage = String.format("POWER_ON,%s,%s", command.getTrayCode(), command.getStoreyCode());
        udpCommandSender.sendCommand(udpMessage);

        // 记录操作日志
        log.info("执行上电操作: 指令ID={}, 托盘={}, 位置={}", commandId, command.getTrayCode(), command.getStoreyCode());
        notifyCommandsUpdate();
    }

    /**
     * 修改机械臂指令
     *
     * @param robotArmCommand 机械臂指令
     * @return 结果
     */
    @Override
    public int updateRobotArmCommand(RobotArmCommand robotArmCommand)
    {
        int i = robotArmCommandMapper.updateRobotArmCommand(robotArmCommand);
        notifyCommandsUpdate();
        return i;
    }

    /**
     * 修改指令执行优先级
     *
     * @param priority 优先级
     * @return 结果
     */
    @Override
    public int updateInstructionExecutionPriority(String priority) {

        redisCache.setCacheObject("sys_config:robotArmPriority", priority);

        return 1;
    }

    /**
     * 批量删除机械臂指令
     *
     * @param robotArmCommandIds 需要删除的机械臂指令ID
     * @return 结果
     */
    @Override
    public int deleteRobotArmCommandByIds(Long[] robotArmCommandIds)
    {
        int i = robotArmCommandMapper.deleteRobotArmCommandByIds(robotArmCommandIds);
        notifyCommandsUpdate();
        return i;
    }

    /**
     * 删除机械臂指令信息
     *
     * @param robotArmCommandId 机械臂指令ID
     * @return 结果
     */
    @Override
    public int deleteRobotArmCommandById(Long robotArmCommandId)
    {
        int i = robotArmCommandMapper.deleteRobotArmCommandById(robotArmCommandId);
        notifyCommandsUpdate();
        return i;
    }

    private void notifyCommandsUpdate() {
        robotArmWebSocketHandler.broadcastCommandUpdate();
    }
}
