package com.zehong.system.task;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.zehong.common.utils.StringUtils;
import com.zehong.system.domain.RobotArmCommand;
import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.RobotArmCommandMapper;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.util.Modbus4jUtils;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import com.zehong.system.service.ITStoreyInfoService;
import com.zehong.system.service.websocket.RobotArmWebSocketHandler;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;

/**
 * @author lenovo
 * @date 2025/6/25
 * @description TODO
 */
@Component
public class FinalExecutionJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(FinalExecutionJob.class);

    @Resource
    private RobotArmWebSocketHandler robotArmWebSocketHandler;
    @Resource
    private ITEquipmentAlarmDataService alarmDataService;
    @Resource
    private ITStoreyInfoService tStoreyInfoService;

    @Resource
    private TStoreyInfoMapper tStoreyInfoMapper;
    @Resource
    private Scheduler scheduler;

    @Resource
    private RobotArmCommandMapper robotArmCommandMapper;
    @Override
    public void execute(JobExecutionContext context) {
        // 1. 初始化变量，避免空指针
        JobDataMap data = null;
        String fStoreyIdStr = null;
        String fPowerOutageIp = null;
        String fPowerOutagePortStr = null;
        Long fStoreyId = null;
        Integer fPowerOutagePort = null;
        TStoreyInfo tStoreyInfo = null;

        try {
            // 2. 提取并校验所有参数
            data = context.getJobDetail().getJobDataMap();
            if (data == null) {
                log.error("JobDataMap为空，终止执行");
                return;
            }
            fStoreyIdStr = data.getString("fStoreyId");
            fPowerOutageIp = data.getString("fPowerOutageIp");
            fPowerOutagePortStr = data.getString("fPowerOutagePort");
            if (StringUtils.isAnyBlank(fStoreyIdStr, fPowerOutageIp, fPowerOutagePortStr)) {
                log.error("参数缺失：fStoreyId={}, ip={}, port={}，终止执行", fStoreyIdStr, fPowerOutageIp, fPowerOutagePortStr);
                return;
            }

            // 3. 参数格式转换（处理NumberFormatException）
            try {
                fStoreyId = Long.parseLong(fStoreyIdStr);
                fPowerOutagePort = Integer.parseInt(fPowerOutagePortStr);
            } catch (NumberFormatException e) {
                log.error("参数格式错误：fStoreyId={}, port={}，终止执行", fStoreyIdStr, fPowerOutagePortStr);
                return;
            }

            // 4. 查询设备信息
            tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
            if (tStoreyInfo == null) {
                log.error("未查询到设备信息：fStoreyId={}，终止执行", fStoreyId);
                return;
            }

            // 5. 执行业务逻辑（Modbus写操作，单独捕获异常）
            String storeyCode = tStoreyInfo.getfStoreyCode();
            if (StringUtils.isBlank(storeyCode)) {
                log.error("设备编码为空：fStoreyId={}，终止执行", fStoreyId);
                return;
            }
            int registerOffsets;
            try {
                registerOffsets = Integer.parseInt(storeyCode.split("-")[1]) - 1;
            } catch (Exception e) {
                log.error("设备编码解析失败：storeyCode={}，终止执行", storeyCode);
                return;
            }
            // Modbus写操作（容错：失败不影响后续清理任务）
            try {
                executeBusinessLogic(fPowerOutageIp, fPowerOutagePort, registerOffsets);
                log.info("Modbus写操作完成：fStoreyId={}", fStoreyId);
            } catch (Exception e) {
                log.error("Modbus写操作异常：fStoreyId={}", fStoreyId, e);
                // 记录告警，但不终止执行（后续清理任务必须执行）
                recordAlarm(tStoreyInfo, "Modbus写操作失败：" + e.getMessage());
            }

            // 6. 更新设备状态（DB操作单独捕获异常）
            try {
                tStoreyInfo.setfStatus("0");
                tStoreyInfo.setfAgingStartTime(null);
                tStoreyInfoService.updateTStoreyInfo(tStoreyInfo);
                log.info("设备状态更新完成：fStoreyId={}", fStoreyId);
            } catch (Exception e) {
                log.error("设备状态更新异常：fStoreyId={}", fStoreyId, e);
                recordAlarm(tStoreyInfo, "设备状态更新失败：" + e.getMessage());
            }

            // 7. 清理任务（Quartz操作单独捕获异常）
            try {
                cleanUpJobs(fStoreyId, context);
                log.info("任务清理完成：fStoreyId={}", fStoreyId);
            } catch (Exception e) {
                log.error("任务清理异常：fStoreyId={}", fStoreyId, e);
                recordAlarm(tStoreyInfo, "任务清理失败：" + e.getMessage());
            }

            // 8. 发送机械臂指令（单独捕获异常）
            try {
                createRoboticArm(tStoreyInfo.getfTrayCode(), storeyCode, tStoreyInfo.getBlankingCommand());
                log.info("机械臂指令发送完成：fStoreyId={}", fStoreyId);
            } catch (Exception e) {
                log.error("机械臂指令发送异常：fStoreyId={}", fStoreyId, e);
                recordAlarm(tStoreyInfo, "机械臂指令发送失败：" + e.getMessage());
            }

            log.info("=== FinalExecutionJob 执行完成：fStoreyId={} ===", fStoreyId);

        } catch (Throwable e) {
            // 9. 捕获所有异常（包括Error）
            log.error("=== FinalExecutionJob 致命异常：fStoreyId={} ===", fStoreyIdStr, e);
            // 记录告警（即使设备信息为空，也尝试记录）
            try {
                if (tStoreyInfo != null && StringUtils.isNotBlank(tStoreyInfo.getfStoreyCode())) {
                    recordAlarm(tStoreyInfo, "最终任务致命异常：" + e.getMessage());
                } else {
                    TEquipmentAlarmData alarm = new TEquipmentAlarmData();
                    alarm.setfAlarmType("03");
                    alarm.setfEquipmentCode(fStoreyIdStr);
                    alarm.setfAlarmData("最终任务致命异常：" + e.getMessage());
                    alarm.setfCreateTime(new Date());
                    alarmDataService.insertTEquipmentAlarmData(alarm);
                }
            } catch (Exception alarmEx) {
                log.error("=== 告警记录失败 ===", alarmEx);
            }
            // 禁止抛出任何异常！！！
        } finally {
            log.error("=== FinalExecutionJob finally：fStoreyId={} ===", fStoreyIdStr);
        }
    }

    // 辅助方法：记录告警（抽离，避免代码重复）
    private void recordAlarm(TStoreyInfo tStoreyInfo, String alarmMsg) {
        try {
            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType("03");
            alarm.setfEquipmentCode(tStoreyInfo.getfStoreyCode());
            alarm.setfAlarmData(alarmMsg);
            alarm.setfCreateTime(new Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
            log.info("告警记录完成：{}", tStoreyInfo.getfStoreyCode());
        } catch (Exception e) {
            log.error("告警记录失败：{}", tStoreyInfo.getfStoreyCode(), e);
        }
    }

    private void createRoboticArm(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();
    }

    private void cleanUpJobs(Long fStoreyId,JobExecutionContext context) throws SchedulerException {
        // 清理通信任务
        JobKey commJobKey = new JobKey("COMM_" + fStoreyId, "DEVICE_TASKS");
        if (scheduler.checkExists(commJobKey)) {
            boolean commDeleted = scheduler.deleteJob(commJobKey);
            log.info("通信任务清理结果：{}（{}）", commDeleted ? "成功" : "失败", commJobKey.getName());
        }
        // 清理自身任务
        JobKey selfJobKey = context.getJobDetail().getKey();
        if (scheduler.checkExists(selfJobKey)) {
            boolean selfDeleted = scheduler.deleteJob(selfJobKey);
            log.info("自身任务清理结果：{}（{}）", selfDeleted ? "成功" : "失败", selfJobKey.getName());
        }
    }

    // ... 业务逻辑方法
    private void executeBusinessLogic(String fPowerOutageIp, int fPowerOutagePort,int registerOffsets) {

        // 10 层
        ModbusMaster master;
        try {
            master = Modbus4jUtils.getMaster(fPowerOutageIp, fPowerOutagePort);
            Boolean aBoolean = Modbus4jUtils.writeCoil(master, 1,registerOffsets,false );
        } catch (ModbusInitException | ModbusTransportException e) {
            throw new RuntimeException(e);
        }
    }
    private void notifyCommandsUpdate() {
        robotArmWebSocketHandler.broadcastCommandUpdate();
    }
}
