package com.zehong.system.task;

import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

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

/**
 * @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";

    // 新增：每个设备任务的Quartz线程隔离（避免任务间干扰）
    private static final String THREAD_GROUP = "DEVICE_THREAD_GROUP";
    /**
     * 创建设备监控任务（入口方法）
     */
    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. 确保调度器已启动（若依框架可能延迟启动）
            if (!scheduler.isStarted()) {
                log.warn("调度器未启动，手动启动...");
                scheduler.start();
            }

            // 关键：添加调度器状态监控，确保线程池可用
            SchedulerMetaData metaData = scheduler.getMetaData();
            log.info("Quartz线程池状态：核心线程={} 任务总数={}",
                    metaData.getThreadPoolSize(),
                    metaData.getNumberOfJobsExecuted());

            // 2. 创建两个核心任务
            createHourlyCommunicationJob(fStoreyId);
            createFinalExecutionJob(fStoreyId, fPowerOutageIp, fPowerOutagePort);

            // 3. 验证任务是否创建成功（关键：打印触发器状态）
            checkTaskStatus(fStoreyId);

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

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

    /**
     * 1. 创建每2分钟执行的通信任务（CronTrigger）
     */
    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 (isJobValid(jobKey, triggerKey)) {
            log.info("通信任务[{}]已存在且有效，跳过创建", jobId);
            return;
        }

        // 1. 构建JobDetail（若依要求：Job类必须是Spring Bean，且无参构造）
        JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
                .withIdentity(jobKey)
                .usingJobData("fStoreyId", fStoreyId.toString())  // 传递参数（String类型避免溢出）
                .storeDurably(false)  // 若依建议：非持久化（触发器删除后Job自动失效）
                .withDescription("设备" + fStoreyId + "每2分钟通信任务")
                .requestRecovery(true)  // 服务重启后恢复未完成的任务
                .build();

        // 2. 构建CronTrigger（每2分钟执行，兼容若依时间解析）
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)  // 显式绑定Job（避免触发器游离）
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?")
                        // 错过触发时：立即执行一次，后续按原计划（关键！）
                        .withMisfireHandlingInstructionFireAndProceed())
                .startNow()  // 立即生效（无需等待第一个周期）
                .build();

        // 3. 原子操作：强制替换旧任务（避免残留状态）
        if (scheduler.checkExists(jobKey)) {
            // 先删除旧任务，再创建新任务（彻底清理状态）
            scheduler.deleteJob(jobKey);
            log.info("通信任务[{}]旧任务已删除，准备创建新任务", jobId);
        }
        Date nextFireTime = scheduler.scheduleJob(job, trigger);
        log.info("通信任务[{}]创建成功，下次执行时间：{}", jobId, nextFireTime);
    }

    /**
     * 2. 创建5分钟后执行的最终任务（SimpleTrigger，仅执行一次）
     */
    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);

        // 若任务已存在且有效，跳过创建
        if (isJobValid(jobKey, triggerKey)) {
            log.info("最终任务[{}]已存在且有效，跳过创建", jobId);
            return;
        }

        // 1. 构建JobDetail
        JobDetail job = JobBuilder.newJob(FinalExecutionJob.class)
                .withIdentity(jobKey)
                .usingJobData("fStoreyId", fStoreyId.toString())
                .usingJobData("fPowerOutageIp", fPowerOutageIp)
                .usingJobData("fPowerOutagePort", fPowerOutagePort.toString())
                .storeDurably(false)
                .requestRecovery(true)
                .build();

        // 2. 构建SimpleTrigger（5分钟后执行，仅一次）
        Date executeTime = Date.from(Instant.now().plus(15, ChronoUnit.MINUTES));
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)  // 显式绑定Job
                .startAt(executeTime)  // 精确执行时间
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        // 错过触发时：立即执行（避免任务失效）
                        .withMisfireHandlingInstructionFireNow()
                        .withRepeatCount(0))  // 仅执行一次（关键！）
                .build();

        // 3. 原子操作：创建/更新任务
        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);
        }
    }

    /**
     * 检查任务是否存在且有效（触发器有下次执行时间）
     */
    private boolean isJobValid(JobKey jobKey, TriggerKey triggerKey) throws SchedulerException {
        // 1. 检查Job和Trigger是否存在
        if (!scheduler.checkExists(jobKey) || !scheduler.checkExists(triggerKey)) {
            log.debug("任务[{}]或触发器[{}]不存在", jobKey.getName(), triggerKey.getName());
            return false;
        }

        // 2. 检查触发器是否有下次执行时间
        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (trigger == null || trigger.getNextFireTime() == null) {
            log.debug("触发器[{}]无效（无下次执行时间）", triggerKey.getName());
            // 清理无效触发器
            scheduler.unscheduleJob(triggerKey);
            return false;
        }

        return true;
    }

    /**
     * 验证任务状态（调试用，打印关键信息）
     */
    private void checkTaskStatus(Long fStoreyId) throws SchedulerException {
        // 检查通信任务
        Trigger commTrigger = scheduler.getTrigger(new TriggerKey("COMM_" + fStoreyId + "_TRIGGER", TRIGGER_GROUP));
        // 检查最终任务
        Trigger finalTrigger = scheduler.getTrigger(new TriggerKey("FINAL_" + fStoreyId + "_TRIGGER", TRIGGER_GROUP));

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