package com.zehong.system.task;

import com.zehong.system.domain.TEquipmentAlarmData;
import com.zehong.system.domain.TStoreyInfo;
import com.zehong.system.mapper.TStoreyInfoMapper;
import com.zehong.system.modbus.business.DeviceStatusReaderAndTimeSetter;
import com.zehong.system.modbus.handler.ModbusResultHandler;
import com.zehong.system.service.ITEquipmentAlarmDataService;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Predicate;

/**
 * @author lenovo
 * @date 2025/6/25
 * @description 上电以后 两分钟执行一次的逻辑
 */
@Component
@DisallowConcurrentExecution
public class DeviceCommunicationJob implements Job {
    private static final Logger log = LoggerFactory.getLogger(DeviceCommunicationJob.class);

    // 常量：超时控制（确保总耗时 < 5分钟Cron周期）
    private static final int SINGLE_PORT_TIMEOUT = 5; // 单个端口通信超时（秒）
    private static final int JOB_TOTAL_TIMEOUT = 15; // Job总超时（秒）
    // 设备ID列表（拆分：按端口分组，避免单次任务过多）
    private static final List<Integer> OFFSETS_501 = Arrays.asList(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27);
    private static final List<Integer> OFFSETS_502 = Arrays.asList(28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54);
    private static final List<Integer> OFFSETS_503 = Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72);

    @Resource
    private ITEquipmentAlarmDataService alarmDataService;
    @Resource
    private DeviceStatusReaderAndTimeSetter deviceStatusReaderAndTimeSetter;
    @Resource
    private TStoreyInfoMapper tStoreyInfoMapper;

    @Autowired
    private ModbusResultHandler resultHandler;

    // 单个端口通信超时时间（确保总时间 < Cron周期）
    private static final int PORT_TIMEOUT_SECONDS = 8;
    // 临时线程池（避免静态线程池关闭问题）
    private final ExecutorService portExecutor = Executors.newFixedThreadPool(3); // 3个端口，3个线程
    @Override
    public void execute(JobExecutionContext context) {
        long startTime = System.currentTimeMillis();
        String fStoreyIdStr = null;
        Long fStoreyId = null;
        TStoreyInfo tStoreyInfo = null;
        String ip = null;

        try {
            log.info("=== DeviceCommunicationJob 启动：线程={}，开始时间={} ===",
                    Thread.currentThread().getName(), new Date(startTime));

            // 1. 提取参数（严格校验，避免后续异常）
            JobDataMap data = context.getJobDetail().getJobDataMap();
            if (data == null) {
                String errMsg = "JobDataMap为空，终止执行";
                log.error(errMsg);
                recordAlarm(null, "fStoreyId未知", errMsg);
                return;
            }
            fStoreyIdStr = data.getString("fStoreyId");
            if (StringUtils.isBlank(fStoreyIdStr)) {
                String errMsg = "fStoreyId参数为空，终止执行";
                log.error(errMsg);
                recordAlarm(null, "fStoreyId为空", errMsg);
                return;
            }

            // 2. 转换参数（处理格式错误）
            try {
                fStoreyId = Long.parseLong(fStoreyIdStr);
            } catch (NumberFormatException e) {
                String errMsg = "fStoreyId格式错误：" + fStoreyIdStr;
                log.error(errMsg, e);
                recordAlarm(null, fStoreyIdStr, errMsg + "：" + e.getMessage());
                return;
            }

            // 3. 查询设备信息（捕获数据库异常，避免穿透）
            try {
                tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);
            } catch (Exception e) {
                String errMsg = "查询设备信息异常：fStoreyId=" + fStoreyId;
                log.error(errMsg, e);
                recordAlarm(null, fStoreyIdStr, errMsg + "：" + e.getMessage());
                return;
            }
            if (tStoreyInfo == null) {
                String errMsg = "未查询到设备信息：fStoreyId=" + fStoreyId;
                log.error(errMsg);
                recordAlarm(null, fStoreyIdStr, errMsg);
                // 清理无效任务（避免后续重复执行）
                try {
                    context.getScheduler().deleteJob(context.getJobDetail().getKey());
                    log.info("清理无效任务：fStoreyId={}", fStoreyId);
                } catch (SchedulerException e) {
                    log.error("清理无效任务失败：fStoreyId={}", fStoreyId, e);
                }
                return;
            }
            ip = tStoreyInfo.getfIp();
            if (StringUtils.isBlank(ip)) {
                String errMsg = "设备IP为空：fStoreyId=" + fStoreyId;
                log.error(errMsg);
                recordAlarm(tStoreyInfo, errMsg, errMsg);
                return;
            }

            // 4. 校验依赖组件（避免空指针）
            if (resultHandler == null) {
                String errMsg = "ModbusResultHandler未初始化：fStoreyId=" + fStoreyId;
                log.error(errMsg);
                recordAlarm(tStoreyInfo, errMsg, errMsg);
                return;
            }
            Predicate<int[]> stopCondition = ModbusResultHandler.createDefaultStopCondition();
            if (stopCondition == null) {
                String errMsg = "Modbus停止条件未初始化：fStoreyId=" + fStoreyId;
                log.error(errMsg);
                recordAlarm(tStoreyInfo, errMsg, errMsg);
                return;
            }

            // 5. 核心逻辑：按端口顺序执行通信（避免并行线程竞争，简化时序）
            log.info("开始设备通信：fStoreyId={}，IP={}，端口501/502/503", fStoreyId, ip);
            executePortCommunication(ip, 501, OFFSETS_501, stopCondition, fStoreyId, tStoreyInfo);
            executePortCommunication(ip, 502, OFFSETS_502, stopCondition, fStoreyId, tStoreyInfo);
            executePortCommunication(ip, 503, OFFSETS_503, stopCondition, fStoreyId, tStoreyInfo);

            // 6. 校验总耗时（确保未超Quartz超时）
            long totalCost = System.currentTimeMillis() - startTime;
            if (totalCost > JOB_TOTAL_TIMEOUT * 1000) {
                String warnMsg = "任务执行超时：fStoreyId=" + fStoreyId + "，耗时=" + totalCost + "ms（阈值=" + JOB_TOTAL_TIMEOUT + "s）";
                log.warn(warnMsg);
                recordAlarm(tStoreyInfo, "任务超时", warnMsg);
            } else {
                log.info("=== DeviceCommunicationJob 成功：fStoreyId={}，总耗时={}ms ===", fStoreyId, totalCost);
            }

        } catch (Throwable e) {
            // 7. 捕获所有异常（确保不传播到Quartz，避免Trigger变ERROR）
            String errMsg = "DeviceCommunicationJob 致命异常：fStoreyIdStr=" + fStoreyIdStr;
            log.error(errMsg, e);
            recordAlarm(tStoreyInfo, fStoreyIdStr, errMsg + "：" + e.getMessage());

        } finally {
            long totalCost = System.currentTimeMillis() - startTime;
            log.info("=== DeviceCommunicationJob 结束：fStoreyIdStr={}，总耗时={}ms ===", fStoreyIdStr, totalCost);
        }
    }
    /**
     * 单个端口通信（同步执行，简化时序，便于超时控制）
     */
    private void executePortCommunication(String ip, int port, List<Integer> deviceIds,
                                          Predicate<int[]> stopCondition, Long fStoreyId, TStoreyInfo tStoreyInfo) {
        long portStartTime = System.currentTimeMillis();
        log.info("端口{}通信开始：fStoreyId={}，设备数={}，开始时间={}",
                port, fStoreyId, deviceIds.size(), new Date(portStartTime));

        try {
            // 调用Modbus服务（同步执行，依赖内部超时控制）
            deviceStatusReaderAndTimeSetter.startMultiDeviceMonitoring(
                    ip, port, deviceIds, resultHandler, stopCondition, SINGLE_PORT_TIMEOUT);

            long portCost = System.currentTimeMillis() - portStartTime;
            log.info("端口{}通信完成：fStoreyId={}，耗时={}ms", port, fStoreyId, portCost);

        } catch (Exception e) {
            long portCost = System.currentTimeMillis() - portStartTime;
            String errMsg = "端口" + port + "通信异常：fStoreyId=" + fStoreyId;
            log.error(errMsg + "，耗时=" + portCost + "ms", e);
            recordAlarm(tStoreyInfo, "端口" + port + "异常", errMsg + "：" + e.getMessage());
        }
    }
    // 简化方法签名，不需要传入ip和fStoreyId参数
    private void executeWithTimeout(Runnable task, long timeout, TimeUnit unit,
                                    TStoreyInfo tStoreyInfo, String timeoutMsg,
                                    String ip, Long fStoreyId) {
        try {
            ExecutorService executor = Executors.newSingleThreadExecutor();
            Future<?> future = executor.submit(task);
            future.get(timeout, unit);
            executor.shutdown();
        } catch (TimeoutException e) {
            log.error("{}：fStoreyId={}, ip={}", timeoutMsg, fStoreyId, ip, e);
            recordAlarm(tStoreyInfo, tStoreyInfo.getfEquipmentCode(),timeoutMsg + "（IP：" + ip + "，设备ID：" + fStoreyId + "）");
        } catch (Exception e) {
            log.error("{}执行异常：fStoreyId={}, ip={}", timeoutMsg, fStoreyId, ip, e);
            recordAlarm(tStoreyInfo, tStoreyInfo.getfEquipmentCode(),timeoutMsg + "执行异常（IP：" + ip + "，设备ID：" + fStoreyId + "）：" + e.getMessage());
        }
    }


    /**
     * 统一告警记录（兼容设备信息为空的场景）
     */
    private void recordAlarm(TStoreyInfo tStoreyInfo, String equipmentCode, String alarmData) {
        try {
            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType("03"); // 老化层告警
            alarm.setfEquipmentCode(tStoreyInfo != null ? tStoreyInfo.getfStoreyCode() : equipmentCode);
            alarm.setfAlarmData(alarmData);
            alarm.setfCreateTime(new Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
        } catch (Exception e) {
            log.error("告警记录失败：{}", alarmData, e);
        }
    }
}
