package com.jiayue.insu.incloud.quartz;

import lombok.NonNull;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.stereotype.Component;

import java.text.ParseException;
import java.util.Date;
import java.util.Map;
/**
 * 定时任务业务类
 *
 * @author yh
 * @version 1.0
 * @since 2022/5/22 11:29
 */
@Component
public class ScheduledTask {

    @Autowired
    private Scheduler scheduler;
    /**
     * 创建或更新定时任务
     *
     * @param t                    定时任务执行类
     * @param jobCode              任务标识(每个人任务标识为一)
     * @param jobRunStartTime      任务执行开始时间，单位：毫秒
     * @param jobRunRepeatInterval 任务重复执行间隔时间，单位：毫秒
     * @param paramMap             任务参数
     * @param <T>                  定时任务执行类必须继承BaseJob
     * @throws SchedulerException 任务创建或更新异常
     * @throws ParseException     cron表达式异常
     */
    public <T extends Job> void scheduleJob( Class<T> t,  final String jobCode,
                                                 final Long jobRunStartTime,  final Long jobRunRepeatInterval,
                                                final Map<String, Object> paramMap) throws SchedulerException, ParseException {
        // 获取触发器
        SimpleTrigger simpleTrigger = this.getSimpleTrigger(jobCode, jobRunStartTime, jobRunRepeatInterval, paramMap);
        // 安排任务
        this.scheduleJob(t, simpleTrigger, jobCode);
        this.resumeJob(jobCode);// 启动任务
    }

    /**
     * 创建或更新定时任务
     *
     * @param t               定时任务执行类
     * @param jobCode         任务标识(每个人任务标识为一)
     * @param jobRunStartTime 任务执行开始时间，单位：毫秒
     * @param cronExpression  任务执行策略
     * @param paramMap        任务参数
     * @param <T>             定时任务执行类必须继承BaseJob
     * @throws SchedulerException 任务创建或更新异常
     * @throws ParseException     cron表达式异常
     */
    public <T extends Job> void scheduleJob( Class<T> t,  final String jobCode,
                                                 final Long jobRunStartTime,  final String cronExpression, final Map<String, Object> paramMap)
            throws SchedulerException, ParseException {
        // 获取触发器
        CronTrigger cronTrigger = this.getCronTrigger(jobCode, jobRunStartTime, cronExpression, paramMap);
        // 安排任务
        this.scheduleJob(t, cronTrigger, jobCode);
        this.resumeJob(jobCode);// 启动任务
    }

    /**
     * 安排定时任务
     *
     * @param t       定时任务执行类
     * @param e       定时器
     * @param jobCode 任务标识
     * @param <T>     定时器实现类
     * @param <E>     触发器实现类
     * @throws SchedulerException 安排定时任务异常
     * @throws ParseException     cron表达式异常
     */
    public <T extends Job, E extends Trigger> void scheduleJob( Class<T> t, @NonNull E e,
                                                                    final String jobCode) throws SchedulerException, ParseException {
        // 如果触发器已存在，则重新安排该任务，否则将创建新的任务安排
        if (scheduler.checkExists(e.getKey())) {
            scheduler.rescheduleJob(e.getKey(), e);
        } else {
            JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean();
            jobDetailFactoryBean.setJobClass(t);
            jobDetailFactoryBean.setName(jobCode);
            jobDetailFactoryBean.afterPropertiesSet();
            JobDetail jobDetail = jobDetailFactoryBean.getObject();
            scheduler.scheduleJob(jobDetail, e);
        }
    }

    /**
     * 删除任务
     *
     * @param jobCode 任务标识
     * @throws SchedulerException 任务删除异常
     */
    public void deleteJob( final String jobCode) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobCode);
        // 如果任务存在，则删除
        if (scheduler.checkExists(jobKey))
            scheduler.deleteJob(jobKey);
    }

    /**
     * 暂停任务
     *
     * @param jobCode 任务标识
     * @throws SchedulerException 任务暂停异常
     */
    public void pauseJob(final String jobCode) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobCode);
        if (scheduler.checkExists(triggerKey))
            scheduler.pauseTrigger(triggerKey);
    }

    /**
     * 重启任务
     *
     * @param jobCode 任务标识
     * @throws SchedulerException 任务重启异常
     */
    public void resumeJob(final String jobCode) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobCode);
        if (scheduler.checkExists(triggerKey))
            scheduler.resumeTrigger(triggerKey);
    }

    /**
     * 获取触发器
     *
     * @param jobCode              触发器标识
     * @param jobRunStartTime      开始执行时间， 单位：毫秒
     * @param jobRunRepeatInterval 重复执行间隔， 单位：毫秒
     * @param paramMap             触发器参数
     * @return 触发器
     * @throws SchedulerException 获取触发器异常
     */
    private SimpleTrigger getSimpleTrigger( final String jobCode, final Long jobRunStartTime,
                                           final Long jobRunRepeatInterval, final Map<String, Object> paramMap) throws SchedulerException {
        SimpleTriggerFactoryBean simpleTriggerFactoryBean = new SimpleTriggerFactoryBean();
        if (paramMap != null)
            simpleTriggerFactoryBean.setJobDataAsMap(paramMap);// 设置任务执行参数
        simpleTriggerFactoryBean.setStartTime(new Date(jobRunStartTime));// 开始执行时间
        simpleTriggerFactoryBean.setRepeatInterval(jobRunRepeatInterval);//重复执行间隔时间 单位毫秒
        simpleTriggerFactoryBean.setName(jobCode);
        simpleTriggerFactoryBean.setMisfireInstruction(
                SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);// 定时任务失火后，将忽略未执行任务，继续下一次任务
        simpleTriggerFactoryBean.afterPropertiesSet();
        return simpleTriggerFactoryBean.getObject();
    }

    /**
     * 获取触发器
     *
     * @param jobCode         触发器标识
     * @param jobRunStartTime 开始执行时间，单位：毫秒
     * @param cronExpression  执行策略表达式
     * @param paramMap        触发器参数
     * @return 触发器
     * @throws SchedulerException 获取触发器异常
     * @throws ParseException     执行策略表达式异常
     */
    private CronTrigger getCronTrigger(final String jobCode, final Long jobRunStartTime,
                                       final String cronExpression, final Map<String, Object> paramMap)
            throws SchedulerException, ParseException {
        CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
        if (paramMap != null)
            cronTriggerFactoryBean.setJobDataAsMap(paramMap);// 设置任务执行参数
        cronTriggerFactoryBean.setStartTime(new Date(jobRunStartTime));// 开始执行时间
        cronTriggerFactoryBean.setCronExpression(cronExpression);// 执行策略
        cronTriggerFactoryBean.setName(jobCode);
        cronTriggerFactoryBean
                .setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);// 定时任务失火后，目前不执行，然后就按照正常的计划执行。
        cronTriggerFactoryBean.afterPropertiesSet();
        return cronTriggerFactoryBean.getObject();
    }

    /**
     * 获取任务最后执行时间
     *
     * @param jobCode 任务标识
     * @return 任务最后执行时间
     * @throws SchedulerException 获取任务最后执行时间异常
     */
    public Date getJobPreviousFireTime(final String jobCode) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobCode);
        Trigger trigger = this.scheduler.getTrigger(triggerKey);
        if (trigger == null)
            throw new SchedulerException(jobCode + "任务不存在!");
        return trigger.getPreviousFireTime();
    }

    /**
     * 获取任务下次执行时间
     *
     * @param jobCode 任务标识
     * @return 任务下次执行时间
     * @throws SchedulerException 获取任务下次执行时间异常
     */
   public Date getJobNextFireTime( final String jobCode) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobCode);
        Trigger trigger = this.scheduler.getTrigger(triggerKey);
        if (trigger == null)
            throw new SchedulerException(jobCode + "任务不存在!");
        return trigger.getNextFireTime();
    }

    /**
     * 获取任务状态
     *
     * @param jobCode 任务标识
     * @return 任务状态，中文描述
     * @throws SchedulerException 获取任务状态异常
     */
    public String getJobStatus( final String jobCode) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(jobCode);
        String triggerStateName = this.scheduler.getTriggerState(triggerKey).name();
        if ("NONE".equals(triggerStateName)) {// 不存在
            return "不存在";
        } else if ("NORMAL".equals(triggerStateName)) {// 正常
            return "正常";
        } else if ("PAUSED".equals(triggerStateName)) {// 暂停
            return "暂停";
        } else if ("COMPLETE".equals(triggerStateName)) {// 完成
            return "完成";
        } else if ("ERROR".equals(triggerStateName)) {// 错误
            return "错误";
        } else if ("BLOCKED".equals(triggerStateName)) {// 阻塞
            return "阻塞";
        }
        return triggerStateName;
    }


}
