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);

    // 超时控制（确保在Cron周期内完成）
    private static final int TOTAL_TIMEOUT_SECONDS = 120; // 2分钟总超时
    private static final int PORT_TIMEOUT_SECONDS = 30;   // 单个端口30秒超时

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

    @Autowired
    private ModbusResultHandler resultHandler;

    @Override
    public void execute(JobExecutionContext context) {
        CompletableFuture<Void> taskFuture = null;
        try {
            // 1. 包装整个任务在CompletableFuture中，便于超时控制
            taskFuture = CompletableFuture.runAsync(() -> {
                executeInternal(context);
            });

            // 2. 设置总超时，避免任务无限期执行
            taskFuture.get(TOTAL_TIMEOUT_SECONDS, TimeUnit.SECONDS);

        } catch (TimeoutException e) {
            log.error("任务执行超时，强制取消");
            if (taskFuture != null) {
                taskFuture.cancel(true);
            }
            // 记录超时告警，但不抛出异常到Quartz
            recordTimeoutAlarm(context);
        } catch (Exception e) {
            log.error("任务执行异常", e);
            // 记录异常，但不传播到Quartz
        }
    }

    private void executeInternal(JobExecutionContext context) {
        long startTime = System.currentTimeMillis();
        String fStoreyIdStr = null;

        try {
            // 参数提取和验证
            JobDataMap data = context.getJobDetail().getJobDataMap();
            fStoreyIdStr = data.getString("fStoreyId");

            if (StringUtils.isBlank(fStoreyIdStr)) {
                log.warn("fStoreyId参数为空，跳过执行");
                return;
            }

            Long fStoreyId = Long.parseLong(fStoreyIdStr);
            TStoreyInfo tStoreyInfo = tStoreyInfoMapper.selectTStoreyInfoById(fStoreyId);

            if (tStoreyInfo == null || StringUtils.isBlank(tStoreyInfo.getfIp())) {
                log.warn("设备信息无效，跳过执行: fStoreyId={}", fStoreyId);
                return;
            }

            // 核心通信逻辑（简化版）
            executeDeviceCommunication(tStoreyInfo, fStoreyId);

            long costTime = System.currentTimeMillis() - startTime;
            log.info("设备通信任务完成: fStoreyId={}, 耗时={}ms", fStoreyId, costTime);

        } catch (Exception e) {
            log.error("设备通信任务执行异常: fStoreyIdStr={}", fStoreyIdStr, e);
            // 仅记录日志，不抛出异常
        }
    }

    private void executeDeviceCommunication(TStoreyInfo tStoreyInfo, Long fStoreyId) {
        String ip = tStoreyInfo.getfIp();

        // 使用并行流简化设备通信（按端口分组并行）
        List<CompletableFuture<Void>> portFutures = Arrays.asList(
                CompletableFuture.runAsync(() ->
                        executeSinglePort(ip, 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), fStoreyId, tStoreyInfo)
                ),
                CompletableFuture.runAsync(() ->
                        executeSinglePort(ip, 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), fStoreyId, tStoreyInfo)
                ),
                CompletableFuture.runAsync(() ->
                        executeSinglePort(ip, 503, Arrays.asList(55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72), fStoreyId, tStoreyInfo)
                )
        );

        // 等待所有端口完成（带超时）
        try {
            CompletableFuture.allOf(portFutures.toArray(new CompletableFuture[0]))
                    .get(PORT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.warn("端口通信超时，取消未完成的任务");
            portFutures.forEach(future -> future.cancel(true));
        } catch (Exception e) {
            log.error("端口通信异常", e);
        }
    }

    private void executeSinglePort(String ip, int port, List<Integer> deviceIds, Long fStoreyId, TStoreyInfo tStoreyInfo) {
        try {
            Predicate<int[]> stopCondition = ModbusResultHandler.createDefaultStopCondition();
            deviceStatusReaderAndTimeSetter.startMultiDeviceMonitoring(
                    ip, port, deviceIds, resultHandler, stopCondition, PORT_TIMEOUT_SECONDS
            );
            log.debug("端口{}通信完成: fStoreyId={}", port, fStoreyId);
        } catch (Exception e) {
            log.error("端口{}通信异常: fStoreyId={}", port, fStoreyId, e);
            recordAlarm(tStoreyInfo, "端口" + port + "通信异常: " + e.getMessage());
        }
    }

    private void recordTimeoutAlarm(JobExecutionContext context) {
        try {
            JobDataMap data = context.getJobDetail().getJobDataMap();
            String fStoreyIdStr = data.getString("fStoreyId");

            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType("03");
            alarm.setfEquipmentCode(fStoreyIdStr != null ? fStoreyIdStr : "unknown");
            alarm.setfAlarmData("设备通信任务执行超时（>" + TOTAL_TIMEOUT_SECONDS + "秒）");
            alarm.setfCreateTime(new Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
        } catch (Exception e) {
            log.error("记录超时告警失败", e);
        }
    }

    private void recordAlarm(TStoreyInfo tStoreyInfo, String alarmData) {
        try {
            TEquipmentAlarmData alarm = new TEquipmentAlarmData();
            alarm.setfAlarmType("03");
            alarm.setfEquipmentCode(tStoreyInfo != null ? tStoreyInfo.getfStoreyCode() : "unknown");
            alarm.setfAlarmData(alarmData);
            alarm.setfCreateTime(new Date());
            alarmDataService.insertTEquipmentAlarmData(alarm);
        } catch (Exception e) {
            log.error("记录告警失败", e);
        }
    }
}
