package com.erp.task.scheduler;

import com.erp.task.entity.ScheduledTask;
import com.erp.task.enums.TaskStatus;
import com.erp.task.repository.ScheduledTaskRepository;
import com.erp.task.service.TaskExecutor;
import com.erp.task.util.CronUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Quartz任务调度器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskScheduler {

    private final Scheduler scheduler;
    private final ScheduledTaskRepository taskRepository;
    private final TaskExecutor taskExecutor;
    private final ApplicationContext applicationContext;

    private final Map<Long, JobKey> taskJobKeys = new ConcurrentHashMap<>();

    /**
     * 调度任务
     */
    public void scheduleJob(ScheduledTask task) {
        try {
            JobKey jobKey = getJobKey(task);
            taskJobKeys.put(task.getId(), jobKey);

            // 如果job已存在，先删除
            if (scheduler.checkExists(jobKey)) {
                scheduler.deleteJob(jobKey);
            }

            // 创建JobDetail
            JobDetail jobDetail = JobBuilder.newJob(TaskQuartzJob.class)
                    .withIdentity(jobKey)
                    .withDescription(task.getDescription())
                    .storeDurably()
                    .build();

            // 注入任务信息
            jobDetail.getJobDataMap().put("taskId", task.getId());
            jobDetail.getJobDataMap().put("taskClass", task.getTaskClass());
            jobDetail.getJobDataMap().put("methodName", task.getMethodName());
            jobDetail.getJobDataMap().put("taskParams", task.getTaskParams());
            jobDetail.getJobDataMap().put("taskType", task.getTaskType());
            jobDetail.getJobDataMap().put("concurrent", task.getConcurrent());
            jobDetail.getJobDataMap().put("applicationContext", applicationContext);

            // 创建Trigger
            CronScheduleBuilder cronBuilder = CronScheduleBuilder
                    .cronExpression(task.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            // 根据Misfire策略设置处理方式
            switch (task.getMisfirePolicy()) {
                case "FIRE_NOW":
                    cronBuilder = CronScheduleBuilder
                            .cronExpression(task.getCronExpression())
                            .withMisfireHandlingInstructionFireAndProceed();
                    break;
                case "FIRE_ONCE":
                    cronBuilder = CronScheduleBuilder
                            .cronExpression(task.getCronExpression())
                            .withMisfireHandlingInstructionIgnoreMisfires();
                    break;
            }

            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(task.getId() + "-trigger", task.getTaskGroup())
                    .withDescription("Trigger for " + task.getTaskName())
                    .withSchedule(cronBuilder)
                    .forJob(jobDetail)
                    .build();

            // 设置并发策略
            if (!task.getConcurrent()) {
                // 不允许并发执行
                jobDetail = jobDetail.getJobBuilder()
                        .disallowConcurrentExecution()
                        .build();
            }

            scheduler.scheduleJob(jobDetail, trigger);

            // 更新下次执行时间
            LocalDateTime nextFireTime = CronUtils.getNextFireTime(task.getCronExpression()).orElse(null);
            taskRepository.updateNextFireTime(task.getId(), nextFireTime);

            log.info("调度任务成功: {}, 下次执行时间: {}", task.getId(), nextFireTime);

        } catch (Exception e) {
            log.error("调度任务失败: {}", task.getId(), e);
            throw new RuntimeException("调度任务失败: " + e.getMessage(), e);
        }
    }

    /**
     * 暂停任务
     */
    public void pauseJob(ScheduledTask task) {
        try {
            JobKey jobKey = getJobKey(task);
            if (scheduler.checkExists(jobKey)) {
                scheduler.pauseJob(jobKey);
                log.info("暂停任务: {}", task.getId());
            }
        } catch (Exception e) {
            log.error("暂停任务失败: {}", task.getId(), e);
        }
    }

    /**
     * 恢复任务
     */
    public void resumeJob(ScheduledTask task) {
        try {
            JobKey jobKey = getJobKey(task);
            if (scheduler.checkExists(jobKey)) {
                scheduler.resumeJob(jobKey);
                log.info("恢复任务: {}", task.getId());
            } else {
                // 如果job不存在，重新调度
                scheduleJob(task);
            }
        } catch (Exception e) {
            log.error("恢复任务失败: {}", task.getId(), e);
        }
    }

    /**
     * 删除任务
     */
    public void deleteJob(ScheduledTask task) {
        try {
            JobKey jobKey = getJobKey(task);
            if (scheduler.checkExists(jobKey)) {
                scheduler.deleteJob(jobKey);
                taskJobKeys.remove(task.getId());
                log.info("删除调度任务: {}", task.getId());
            }
        } catch (Exception e) {
            log.error("删除调度任务失败: {}", task.getId(), e);
        }
    }

    /**
     * 立即执行一次
     */
    public void executeOnce(ScheduledTask task) {
        taskExecutor.execute(task);
    }

    /**
     * 触发Misfire
     */
    public void triggerMisfire(ScheduledTask task) {
        try {
            JobKey jobKey = getJobKey(task);
            if (scheduler.checkExists(jobKey)) {
                scheduler.triggerJob(jobKey);
                log.info("触发Misfire任务: {}", task.getId());
            }
        } catch (Exception e) {
            log.error("触发Misfire失败: {}", task.getId(), e);
        }
    }

    /**
     * 获取JobKey
     */
    private JobKey getJobKey(ScheduledTask task) {
        return JobKey.jobKey(task.getId() + "-job", task.getTaskGroup());
    }

    /**
     * Quartz Job实现
     */
    @Slf4j
    public static class TaskQuartzJob extends QuartzJobBean {

        private Long taskId;
        private String taskClass;
        private String methodName;
        private String taskParams;
        private String taskType;
        private Boolean concurrent;
        private ApplicationContext applicationContext;

        @Override
        protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
            JobDataMap jobDataMap = context.getMergedJobDataMap();

            this.taskId = jobDataMap.getLong("taskId");
            this.taskClass = (String) jobDataMap.get("taskClass");
            this.methodName = (String) jobDataMap.get("methodName");
            this.taskParams = (String) jobDataMap.get("taskParams");
            this.taskType = (String) jobDataMap.get("taskType");
            this.concurrent = (Boolean) jobDataMap.get("concurrent");
            this.applicationContext = (ApplicationContext) jobDataMap.get("applicationContext");

            log.info("Quartz触发任务执行: taskId={}", taskId);

            try {
                // 从数据库获取最新任务信息
                ScheduledTaskRepository repo = applicationContext.getBean(ScheduledTaskRepository.class);
                ScheduledTask task = repo.findById(taskId).orElse(null);

                if (task != null && task.getStatus() == TaskStatus.RUNNING) {
                    TaskExecutor executor = applicationContext.getBean(TaskExecutor.class);
                    executor.execute(task);
                } else {
                    log.warn("任务不存在或未运行: {}", taskId);
                }
            } catch (Exception e) {
                log.error("执行Quartz任务失败: {}", taskId, e);
                throw new JobExecutionException(e);
            }
        }
    }
}
