package com.ruoyi.ems.task;

import com.ruoyi.ems.domain.InspectionPlan;
import com.ruoyi.ems.domain.InspectionReport;
import com.ruoyi.ems.mapper.InspectionPlanMapper;
import com.ruoyi.ems.service.IInspectionPlanService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * 巡检调度器
 *
 * 【重要】此类需要根据实际项目情况进行调整
 * 主要功能：
 * 1. 应用启动时加载所有启用调度的巡检计划
 * 2. 根据Cron表达式定时执行巡检任务
 * 3. 提供动态注册/注销计划的能力
 * 4. 提供调度器状态查询
 */
@Component
public class InspectionScheduler {

    private static final Logger log = LoggerFactory.getLogger(InspectionScheduler.class);

    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Autowired
    private InspectionPlanMapper planMapper;

    @Autowired
    private IInspectionPlanService planService;

    /** 任务调度器 */
    private ThreadPoolTaskScheduler taskScheduler;

    /** 已注册的任务 Map<planCode, ScheduledFuture> */
    private final ConcurrentHashMap<String, ScheduledFuture<?>> scheduledTasks = new ConcurrentHashMap<>();

    /** 已注册的计划信息 Map<planCode, InspectionPlan> */
    private final ConcurrentHashMap<String, InspectionPlan> registeredPlans = new ConcurrentHashMap<>();

    /** 正在执行的计划 Set<planCode> */
    private final Set<String> executingPlans = ConcurrentHashMap.newKeySet();

    /** 启动时间 */
    private LocalDateTime startTime;

    /**
     * 初始化调度器
     */
    @PostConstruct
    public void init() {
        log.info("========== 初始化巡检调度器 ==========");

        // 创建任务调度器
        taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(5);
        taskScheduler.setThreadNamePrefix("inspection-scheduler-");
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setAwaitTerminationSeconds(60);
        taskScheduler.initialize();

        startTime = LocalDateTime.now();

        // 加载已启用的计划
        loadEnabledPlans();

        log.info("========== 巡检调度器初始化完成，已注册 {} 个计划 ==========", scheduledTasks.size());
    }

    /**
     * 销毁调度器
     */
    @PreDestroy
    public void destroy() {
        log.info("========== 销毁巡检调度器 ==========");

        // 取消所有任务
        scheduledTasks.values().forEach(future -> future.cancel(false));
        scheduledTasks.clear();
        registeredPlans.clear();

        // 关闭调度器
        if (taskScheduler != null) {
            taskScheduler.shutdown();
        }

        log.info("========== 巡检调度器已销毁 ==========");
    }

    /**
     * 加载所有已启用调度的计划
     */
    public void loadEnabledPlans() {
        // 先清空现有任务
        scheduledTasks.values().forEach(future -> future.cancel(false));
        scheduledTasks.clear();
        registeredPlans.clear();

        // 查询已启用调度的计划
        List<InspectionPlan> plans = planMapper.selectEnabledSchedulePlans();

        if (plans == null || plans.isEmpty()) {
            log.info("没有需要加载的巡检计划");
            return;
        }

        for (InspectionPlan plan : plans) {
            try {
                doRegisterPlan(plan);
            } catch (Exception e) {
                log.error("注册巡检计划失败: planCode={}, error={}", plan.getPlanCode(), e.getMessage());
            }
        }
    }

    /**
     * 注册计划到调度器
     */
    public void registerPlan(String planCode) {
        InspectionPlan plan = planMapper.selectByPlanCode(planCode);
        if (plan == null) {
            throw new RuntimeException("计划不存在: " + planCode);
        }
        registerPlan(plan);
    }

    /**
     * 注册计划到调度器
     */
    public void registerPlan(InspectionPlan plan) {
        if (plan == null || StringUtils.isBlank(plan.getPlanCode())) {
            throw new RuntimeException("计划信息不完整");
        }

        // 先注销旧的任务
        unregisterPlan(plan.getPlanCode());

        // 检查是否可调度
        if (plan.getPlanType() != 2) {
            log.warn("计划[{}]不是自动巡检类型，跳过注册", plan.getPlanCode());
            return;
        }

        if (StringUtils.isBlank(plan.getCronExpression())) {
            log.warn("计划[{}]未配置Cron表达式，跳过注册", plan.getPlanCode());
            return;
        }

        doRegisterPlan(plan);

        // 更新数据库调度状态
        planMapper.updateScheduleEnabled(plan.getPlanCode(), 1);
    }

    /**
     * 实际注册任务
     */
    private void doRegisterPlan(InspectionPlan plan) {
        String planCode = plan.getPlanCode();
        String cron = plan.getCronExpression();

        // 验证Cron表达式
        if (!CronExpression.isValidExpression(cron)) {
            throw new RuntimeException("无效的Cron表达式: " + cron);
        }

        // 创建定时任务
        Runnable task = () -> executeInspection(planCode);

        ScheduledFuture<?> future = taskScheduler.schedule(task, new CronTrigger(cron));

        scheduledTasks.put(planCode, future);
        registeredPlans.put(planCode, plan);

        // 计算并更新下次执行时间
        Date nextExecTime = calculateNextExecTime(cron);
        planMapper.updateExecInfo(planCode, null, nextExecTime);

        log.info("巡检计划[{}]已注册到调度器，Cron: {}, 下次执行: {}",
            planCode, cron, nextExecTime != null ? DATETIME_FORMATTER.format(
                LocalDateTime.ofInstant(nextExecTime.toInstant(), ZoneId.systemDefault())) : "未知");
    }

    /**
     * 从调度器注销计划
     */
    public void unregisterPlan(String planCode) {
        ScheduledFuture<?> future = scheduledTasks.remove(planCode);
        if (future != null) {
            future.cancel(false);
            log.info("巡检计划[{}]已从调度器注销", planCode);
        }
        registeredPlans.remove(planCode);

        // 更新数据库调度状态
        planMapper.updateScheduleEnabled(planCode, 0);
    }

    /**
     * 刷新计划配置
     */
    public void refreshPlan(String planCode) {
        InspectionPlan plan = planMapper.selectByPlanCode(planCode);
        if (plan == null) {
            unregisterPlan(planCode);
            return;
        }

        // 检查是否应该注册
        if (plan.getPlanType() == 2
            && plan.getScheduleEnabled() != null
            && plan.getScheduleEnabled() == 1
            && StringUtils.isNotBlank(plan.getCronExpression())) {
            registerPlan(plan);
        } else {
            unregisterPlan(planCode);
        }
    }

    /**
     * 手动触发一次巡检
     */
    public InspectionReport triggerInspection(String planCode) {
        return executeInspection(planCode);
    }

    /**
     * 执行巡检任务
     */
    private InspectionReport executeInspection(String planCode) {
        if (executingPlans.contains(planCode)) {
            log.warn("计划[{}]正在执行中，跳过本次触发", planCode);
            return null;
        }

        executingPlans.add(planCode);
        log.info("========== 开始执行巡检任务: {} ==========", planCode);

        try {
            // 调用Service执行巡检
            InspectionReport report = planService.executeAutoInspection(planCode, "system");

            // 更新执行信息
            InspectionPlan plan = registeredPlans.get(planCode);
            if (plan != null) {
                Date nextExecTime = calculateNextExecTime(plan.getCronExpression());
                planMapper.updateExecInfo(planCode, new Date(), nextExecTime);
            }

            log.info("========== 巡检任务执行完成: {}, 报告: {} ==========",
                planCode, report != null ? report.getReportCode() : "null");

            return report;
        } catch (Exception e) {
            log.error("巡检任务执行异常: planCode={}, error={}", planCode, e.getMessage(), e);
            throw new RuntimeException("巡检执行失败: " + e.getMessage(), e);
        } finally {
            executingPlans.remove(planCode);
        }
    }

    /**
     * 计算下次执行时间
     */
    private Date calculateNextExecTime(String cron) {
        if (StringUtils.isBlank(cron)) {
            return null;
        }
        try {
            CronExpression expression = CronExpression.parse(cron);
            LocalDateTime next = expression.next(LocalDateTime.now());
            return next != null ? Date.from(next.atZone(ZoneId.systemDefault()).toInstant()) : null;
        } catch (Exception e) {
            log.warn("计算下次执行时间失败: cron={}, error={}", cron, e.getMessage());
            return null;
        }
    }

    /**
     * 获取调度器状态
     *
     * 【重要】前端调度器状态组件依赖此方法返回的数据结构
     */
    public Map<String, Object> getStatus() {
        Map<String, Object> status = new HashMap<>();

        // 基本状态
        status.put("running", taskScheduler != null && !taskScheduler.getScheduledThreadPoolExecutor().isShutdown());
        status.put("registeredCount", scheduledTasks.size());
        status.put("executingCount", executingPlans.size());
        status.put("startTime", startTime != null ? DATETIME_FORMATTER.format(startTime) : null);

        // 线程池状态
        if (taskScheduler != null) {
            ScheduledThreadPoolExecutor executor = taskScheduler.getScheduledThreadPoolExecutor();
            status.put("threadPoolStatus", String.format("活跃线程: %d, 池大小: %d, 队列任务: %d",
                executor.getActiveCount(), executor.getPoolSize(), executor.getQueue().size()));
        }

        // 已注册的计划列表
        List<Map<String, Object>> planList = new ArrayList<>();
        for (Map.Entry<String, InspectionPlan> entry : registeredPlans.entrySet()) {
            InspectionPlan plan = entry.getValue();
            Map<String, Object> planInfo = new HashMap<>();
            planInfo.put("planCode", plan.getPlanCode());
            planInfo.put("planName", plan.getPlanName());
            planInfo.put("cronExpression", plan.getCronExpression());
            planInfo.put("executing", executingPlans.contains(plan.getPlanCode()));

            // 计算下次执行时间
            Date nextExec = calculateNextExecTime(plan.getCronExpression());
            planInfo.put("nextExecTime", nextExec != null ?
                DATETIME_FORMATTER.format(LocalDateTime.ofInstant(nextExec.toInstant(), ZoneId.systemDefault())) : null);

            planList.add(planInfo);
        }
        status.put("registeredPlans", planList);

        return status;
    }

    /**
     * 获取已注册计划数量
     */
    public int getRegisteredCount() {
        return scheduledTasks.size();
    }

    /**
     * 检查计划是否已注册
     */
    public boolean isRegistered(String planCode) {
        return scheduledTasks.containsKey(planCode);
    }

    /**
     * 检查计划是否正在执行
     */
    public boolean isExecuting(String planCode) {
        return executingPlans.contains(planCode);
    }
}