package com.zehong.system.task;

import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
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;
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";

    // 新增：每个设备任务的Quartz线程隔离（避免任务间干扰）
    private static final String THREAD_GROUP = "DEVICE_THREAD_GROUP";

    // 常量：Cron周期（5分钟）、任务有效期（7天）
    private static final String CRON_EXPRESSION = "0 0/5 * * * ?";
    private static final int TASK_VALID_DAYS = 7;
    /**
     * 创建设备监控任务（入口：增加调度器健康检查、任务去重）
     */
    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. 任务去重（避免重复创建导致资源竞争）
            if (isTaskExists("COMM_" + fStoreyId)) {
                log.info("通信任务[COMM_{}]已存在，无需重复创建", fStoreyId);
                // 检查现有触发器状态，若为ERROR则重建
                if (isTriggerError("COMM_" + fStoreyId)) {
                    log.warn("通信任务[COMM_{}]触发器状态为ERROR，重建触发器", fStoreyId);
                    createHourlyCommunicationJob(fStoreyId);
                }
                return;
            }

            // 3. 创建核心任务
            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);
        }
    }

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

        // 1. 构建JobDetail（仅元数据，禁止并发执行通过注解控制）
        JobDetail job = JobBuilder.newJob(DeviceCommunicationJob.class)
                .withIdentity(jobKey)
                .withDescription("设备" + fStoreyId + "每5分钟Modbus通信任务")
                .usingJobData("fStoreyId", fStoreyId.toString()) // 传递参数（String避免类型问题）
                .storeDurably(false) // 触发器删除后Job自动失效
                .requestRecovery(true) // 服务重启后恢复未完成任务
                .build();

        // 2. 构建CronTrigger（移除withMisfireThreshold，兼容低版本）
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .withDescription("设备" + fStoreyId + "通信任务触发器（每5分钟）")
                .withSchedule(CronScheduleBuilder.cronSchedule(CRON_EXPRESSION)
                        // Misfire策略：错过触发后立即执行，再按原计划（低版本支持）
                        .withMisfireHandlingInstructionFireAndProceed())
                .startNow()
                .endAt(Date.from(Instant.now().plus(TASK_VALID_DAYS, ChronoUnit.DAYS)))
                .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);
        }
    }


    /**
     * 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(15, ChronoUnit.MINUTES));
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey)
                .forJob(jobKey)
                .withDescription("设备" + fStoreyId + "最终任务触发器")
                .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();
        }

        SchedulerMetaData metaData = scheduler.getMetaData();
        // 低版本兼容：获取线程池大小和已执行任务数（替代活跃线程数）
        int poolSize = metaData.getThreadPoolSize();
        long executedJobs = metaData.getNumberOfJobsExecuted();
        log.info("Quartz健康状态：线程池大小={}, 已执行任务数={}", poolSize, executedJobs);

        // 线程池大小预警（根据实际需求调整阈值）
        if (poolSize < 5) {
            log.warn("Quartz线程池过小（当前={}），可能导致任务延迟", poolSize);
        }
    }
    /**
     * 检查触发器是否为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 boolean isJobValid(JobKey jobKey, TriggerKey triggerKey) throws SchedulerException {
        if (!scheduler.checkExists(jobKey) || !scheduler.checkExists(triggerKey)) {
            log.debug("任务[{}]或触发器[{}]不存在", jobKey.getName(), triggerKey.getName());
            return false;
        }

        Trigger trigger = scheduler.getTrigger(triggerKey);
        Trigger.TriggerState state = scheduler.getTriggerState(triggerKey);
        // 触发器状态为NORMAL且有下次执行时间，才视为有效
        if (trigger == null || trigger.getNextFireTime() == null || state != Trigger.TriggerState.NORMAL) {
            log.debug("触发器[{}]无效：状态={}, 下次执行时间={}",
                    triggerKey.getName(), state, trigger != null ? trigger.getNextFireTime() : "null");
            // 清理无效触发器（保留Job，避免重建）
            scheduler.unscheduleJob(triggerKey);
            return false;
        }
        return true;
    }
    /**
     * 验证任务状态（增强日志详情）
     */
    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() : "不存在");
    }

}
