package com.zehong.system.task;

import com.zehong.quartz.config.QuartzTaskMonitor;
import com.zehong.system.task.DeviceCommJob.DeviceComm501Device1Job;
import com.zehong.system.task.DeviceCommJob.DeviceComm501Device2Job;
import com.zehong.system.task.DeviceCommJob.DeviceComm501Device3Job;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Set;

/**
 * @author lenovo
 * @date 2025/6/25
 * @description 定时调度任务
 */
@Service
public class DeviceTaskScheduler {

    private static final Logger log = LoggerFactory.getLogger(DeviceTaskScheduler.class);
    @Resource
    private Scheduler scheduler;

    // 任务组名（统一固定，与原有逻辑保持一致）
    private static final String JOB_GROUP = "DEVICE_TASKS";
    // 触发器组名（统一固定，与原有逻辑保持一致）
    private static final String TRIGGER_GROUP = "DEVICE_TRIGGERS";
    // 新增：注入监控器
    @Autowired
    private QuartzTaskMonitor quartzTaskMonitor;
//    @PostConstruct
//    public void init() throws SchedulerException {
//        scheduler.getListenerManager().addJobListener(new QuartzJobListener());
//    }

    /**
     * 创建设备监控任务（入口：增加调度器健康检查、任务去重）
     */
    public void scheduleDeviceMonitoring(Long fStoreyId, String fPowerOutageIp, Integer fPowerOutagePort) {
        if (fStoreyId == null || fPowerOutageIp == null || fPowerOutagePort == null) {
            log.error("任务参数为空：fStoreyId={}, ip={}, port={}", fStoreyId, fPowerOutageIp, fPowerOutagePort);
            throw new RuntimeException("任务参数不可为空");
        }

        try {
            log.info("=== 开始创建设备监控任务：fStoreyId={} ===", fStoreyId);

            // 1. 调度器健康检查（确保线程池可用）
            checkSchedulerHealth();

            // 2. 创建任务前的状态监控
            quartzTaskMonitor.monitorTaskStatus(fStoreyId);

            // 2. 清理可能的残留任务
            cleanExistingTasks(fStoreyId);
            // 3. 创建核心任务
            // 1. 创建3个端口专属任务（501:5分钟后，502:10分钟后，503:15分钟后）
//            createPortSpecificCommJobs(fStoreyId);

            createHourlyCommunicationJob(fStoreyId);

            createFinalExecutionJob(fStoreyId, fPowerOutageIp, fPowerOutagePort);
            checkTaskStatus(fStoreyId);

            log.info("=== 设备监控任务创建完成：fStoreyId={} ===", fStoreyId);

        } catch (SchedulerException e) {
            log.error("=== 创建设备监控任务失败：fStoreyId={} ===", fStoreyId, e);
            cleanInvalidTask(fStoreyId); // 失败时清理残留
            throw new RuntimeException("Quartz任务调度失败", e);
        }
    }

    /**
     * 清理已存在的任务 - 修复版本
     */
    public void cleanExistingTasks(Long fStoreyId) throws SchedulerException {
        // 清理所有与这个fStoreyId相关的任务
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(JOB_GROUP));
        for (JobKey jobKey : jobKeys) {
            if (jobKey.getName().contains(fStoreyId.toString())) {
                scheduler.deleteJob(jobKey);
                log.info("清理现有任务: {}", jobKey.getName());
            }
        }

        // 清理触发器
        Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(GroupMatcher.triggerGroupEquals(TRIGGER_GROUP));
        for (TriggerKey triggerKey : triggerKeys) {
            if (triggerKey.getName().contains(fStoreyId.toString())) {
                scheduler.unscheduleJob(triggerKey);
                log.info("清理现有触发器: {}", triggerKey.getName());
            }
        }
    }
    // -------------------------- 新增：创建3个端口专属任务 --------------------------
    /**
     * 为3个端口Job创建一次性SimpleTrigger：
     * - 501Job：当前时间+5分钟
     * - 502Job：当前时间+10分钟
     * - 503Job：当前时间+15分钟
     */
    private void createPortSpecificCommJobs(Long fStoreyId) throws SchedulerException {
        // 1. 端口501：5分钟后执行
        createPortCommJob(fStoreyId, 501, 1,DeviceComm501Device1Job.class, 2);
        // 2. 端口502：10分钟后执行
        createPortCommJob(fStoreyId, 501, 2,DeviceComm501Device2Job.class, 4);
//        // 3. 端口503：15分钟后执行
        createPortCommJob(fStoreyId, 501, 3,DeviceComm501Device3Job.class, 6);
    }
    /**
     * 单个端口Job和Trigger创建（通用方法）
     * @param fStoreyId 设备ID
     * @param port 端口号
     * @param jobClass Job类
     * @param delayMin 延迟执行时间（分钟）
     */
    private void createPortCommJob(Long fStoreyId, int port, int deviceId,Class<? extends Job> jobClass, int delayMin) throws SchedulerException {
        String jobId = String.format("COMM_%d_%d_%d", port, deviceId, fStoreyId);
        String triggerId = String.format("TRIGGER_%d_%d_%d", port, deviceId, fStoreyId);

        JobKey jobKey = new JobKey(jobId, JOB_GROUP);
        TriggerKey triggerKey = new TriggerKey(triggerId, TRIGGER_GROUP);

        // 清理现有任务
        if (scheduler.checkExists(triggerKey)) {
            scheduler.unscheduleJob(triggerKey);
        }
        if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
        }

        // 关键修复：使用正确的数据类型传递参数
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("fStoreyId", fStoreyId);        // 直接传递Long
        jobDataMap.put("port", port);                  // 直接传递int
        jobDataMap.put("deviceId", deviceId);          // 直接传递int

        JobDetail job = JobBuilder.newJob(jobClass)
                .withIdentity(jobKey)
                .usingJobData(jobDataMap)  // 使用JobDataMap而不是逐个设置
                .storeDurably(false)
                .build();

        Date triggerTime = Date.from(Instant.now().plus(delayMin, ChronoUnit.MINUTES));

        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .withDescription("设备" + fStoreyId + "最终任务触发器，触发时间是:" + triggerTime)
                .startAt(triggerTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withRepeatCount(0)  // 只执行一次
                        .withMisfireHandlingInstructionFireNow())
                .build();

        Date nextFireTime = scheduler.scheduleJob(job, trigger);
        log.info("端口[{}]设备[{}]任务创建成功：延迟{}分钟，执行时间：{}",
                port, deviceId, delayMin, nextFireTime);
    }
    /**
     * 单个端口Job和Trigger创建（通用方法）
     * @param fStoreyId 设备ID
     * @param port 端口号
     * @param jobClass Job类
     * @param delayMin 延迟执行时间（分钟）
     */
    private void createPortCommJob(Long fStoreyId, int port, Class<? extends Job> jobClass, int delayMin) throws SchedulerException {
        String jobId = "COMM_" + port + "_" + fStoreyId; // JobID：COMM_501_123（端口+设备ID，确保唯一）
        String triggerId = "TRIGGER_" + port + "_" + fStoreyId; // TriggerID：TRIGGER_501_123

        JobKey jobKey = new JobKey(jobId, JOB_GROUP);
        TriggerKey triggerKey = new TriggerKey(triggerId, TRIGGER_GROUP);

        // 1. 去重：先删除旧任务/触发器
        if (scheduler.checkExists(triggerKey)) {
            scheduler.unscheduleJob(triggerKey);
            log.info("端口[{}]旧触发器已删除：{}", port, triggerId);
        }
        if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
            log.info("端口[{}]旧任务已删除：{}", port, jobId);
        }

        // 2. 创建JobDetail
        JobDetail job = JobBuilder.newJob(jobClass)
                .withIdentity(jobKey)
                .usingJobData("fStoreyId", fStoreyId.toString()) // 传递设备ID
                .storeDurably(false)
                .build();

        // 3. 创建一次性SimpleTrigger（延迟delayMin分钟，仅执行1次）
        Date triggerTime = Date.from(Instant.now().plus(delayMin, ChronoUnit.MINUTES));
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .startAt(triggerTime) // 延迟执行时间
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withRepeatCount(0) // 仅执行1次
                        .withMisfireHandlingInstructionFireNow()) // 错过则立即执行
                .build();

        // 4. 提交调度
        Date nextFireTime = scheduler.scheduleJob(job, trigger);
        log.info("端口[{}]任务创建成功：jobId={}，延迟{}分钟，下次执行：{}",
                port, jobId, delayMin, nextFireTime);
    }

    /**
     * 1. 创建每5分钟执行的通信任务（核心优化：简化调度逻辑、调整Misfire策略）
     */
    private void createHourlyCommunicationJob(Long fStoreyId) throws SchedulerException {
        String jobId = "COMM_" + fStoreyId;
        JobKey jobKey = new JobKey(jobId, JOB_GROUP);
        TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);

        // 先删除旧的触发器和任务（彻底清理）
        if (scheduler.checkExists(triggerKey)) {
            scheduler.unscheduleJob(triggerKey);
        }
        if (scheduler.checkExists(jobKey)) {
            scheduler.deleteJob(jobKey);
        }

        JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
                .withIdentity(jobKey)
                .usingJobData("fStoreyId", fStoreyId.toString())
                .storeDurably(false)
                .build();

        Date executeTime = Date.from(Instant.now().plus(5, ChronoUnit.MINUTES));
        // 关键修复：使用StartAt而不是StartNow
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .withDescription("设备" + fStoreyId + "最终任务触发器，触发时间是:" + executeTime)
                .startAt(executeTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withMisfireHandlingInstructionFireNow() // 错过立即执行
                        .withRepeatCount(0)) // 仅一次
                .build();
//        CronTrigger trigger = TriggerBuilder.newTrigger()
//                .withIdentity(triggerKey)
//                .forJob(jobKey)
//                .withDescription("设备" + fStoreyId + "写时间任务，触发时间是:" + executeTime)
//                .withSchedule(CronScheduleBuilder.cronSchedule("0 0/3 * * * ?")  // 改为3分钟
//                        .withMisfireHandlingInstructionDoNothing())
//                .startAt(new Date())  // 显式设置开始时间:cite[1]
//                .build();

        Date nextFireTime = scheduler.scheduleJob(job, trigger);
        log.info("通信任务[{}]创建成功，下次执行：{}", jobId, nextFireTime);
    }

    /**
     * 2. 创建15分钟后执行的最终任务（保持原逻辑，优化超时）
     */
    private void createFinalExecutionJob(Long fStoreyId, String fPowerOutageIp, Integer fPowerOutagePort) throws SchedulerException {
        String jobId = "FINAL_" + fStoreyId;
        JobKey jobKey = new JobKey(jobId, JOB_GROUP);
        TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);

        JobDetail job = JobBuilder.newJob(FinalExecutionJob.class)
                .withIdentity(jobKey)
                .withDescription("设备" + fStoreyId + "最终执行任务（仅一次）")
                .usingJobData("fStoreyId", fStoreyId.toString())
                .usingJobData("fPowerOutageIp", fPowerOutageIp)
                .usingJobData("fPowerOutagePort", fPowerOutagePort.toString())
                .storeDurably(false)
                .requestRecovery(true)
                .build();

        Date executeTime = Date.from(Instant.now().plus(7, ChronoUnit.MINUTES));
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .withDescription("设备" + fStoreyId + "最终任务触发器，触发时间是:" + executeTime)
                .startAt(executeTime)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        .withMisfireHandlingInstructionFireNow() // 错过立即执行
                        .withRepeatCount(0)) // 仅一次
                .build();

        if (scheduler.checkExists(jobKey)) {
            Date nextFireTime = scheduler.rescheduleJob(triggerKey, trigger);
            log.info("最终任务[{}]更新触发器成功，执行时间：{}", jobId, nextFireTime);
        } else {
            Date nextFireTime = scheduler.scheduleJob(job, trigger);
            log.info("最终任务[{}]创建成功，执行时间：{}", jobId, nextFireTime);
        }
    }
    // ------------------------------ 工具方法：增强稳定性 ------------------------------
    /**
     * 检查Quartz调度器健康状态（避免线程池耗尽）
     */
    private void checkSchedulerHealth() throws SchedulerException {
        if (!scheduler.isStarted()) {
            log.warn("调度器未启动，手动启动...");
            scheduler.start();
        }

        if (scheduler.isInStandbyMode()) {
            log.warn("调度器处于待机模式，启动...");
            scheduler.start();
        }

        log.info("调度器状态: 已启动={}, 待机模式={}",
                scheduler.isStarted(), scheduler.isInStandbyMode());
    }
    /**
     * 检查触发器是否为ERROR状态
     */
    private boolean isTriggerError(String jobId) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobId + "_TRIGGER", TRIGGER_GROUP);
        if (!scheduler.checkExists(triggerKey)) {
            return false;
        }
        Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
        return state == Trigger.TriggerState.ERROR;
    }
    /**
     * 检查任务是否已存在（避免重复创建）
     */
    private boolean isTaskExists(String jobId) throws SchedulerException {
        Set<JobKey> jobKeys = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(JOB_GROUP));
        for (JobKey key : jobKeys) {
            if (key.getName().equals(jobId)) {
                return true;
            }
        }
        return false;
    }
    /**
     * 清理无效任务（创建失败时避免残留）
     */
    private void cleanInvalidTask(Long fStoreyId) {
        try {
            JobKey commJobKey = new JobKey("COMM_" + fStoreyId, JOB_GROUP);
            JobKey finalJobKey = new JobKey("FINAL_" + fStoreyId, JOB_GROUP);
            if (scheduler.checkExists(commJobKey)) {
                scheduler.deleteJob(commJobKey);
                log.info("清理无效通信任务：fStoreyId={}", fStoreyId);
            }
            if (scheduler.checkExists(finalJobKey)) {
                scheduler.deleteJob(finalJobKey);
                log.info("清理无效最终任务：fStoreyId={}", fStoreyId);
            }
        } catch (SchedulerException e) {
            log.error("清理无效任务失败：fStoreyId={}", fStoreyId, e);
        }
    }
    /**
     * 验证任务状态（增强日志详情）
     */
    private void checkTaskStatus(Long fStoreyId) throws SchedulerException {
        TriggerKey commTriggerKey = new TriggerKey("COMM_" + fStoreyId + "_TRIGGER", TRIGGER_GROUP);
        TriggerKey finalTriggerKey = new TriggerKey("FINAL_" + fStoreyId + "_TRIGGER", TRIGGER_GROUP);

        Trigger commTrigger = scheduler.getTrigger(commTriggerKey);
        Trigger finalTrigger = scheduler.getTrigger(finalTriggerKey);

        log.info("=== 任务状态验证：fStoreyId={} ===", fStoreyId);
        log.info("通信任务：状态={}, 下次执行={}, 过期时间={}",
                commTrigger != null ? scheduler.getTriggerState(commTriggerKey) : "不存在",
                commTrigger != null ? commTrigger.getNextFireTime() : "不存在",
                commTrigger != null ? commTrigger.getEndTime() : "不存在");
        log.info("最终任务：状态={}, 执行时间={}, 过期时间={}",
                finalTrigger != null ? scheduler.getTriggerState(finalTriggerKey) : "不存在",
                finalTrigger != null ? finalTrigger.getNextFireTime() : "不存在",
                finalTrigger != null ? finalTrigger.getEndTime() : "不存在");
    }
}
