package com.codvision.base.support;

import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author KX
 * @date 2022/3/8 14:47
 */
@Component
@Slf4j
public class QuartzJobManager {

    private static QuartzJobManager jobUtil;

    @Resource
    private Scheduler scheduler;

    public QuartzJobManager() {
        log.info("init jobUtil");
        jobUtil = this;
    }

    public static QuartzJobManager getInstance() {
        log.info("return  JobCreateUtil");
        return QuartzJobManager.jobUtil;
    }

    /**
     * 创建job
     *
     * @param jobClass       任务/触发器 类
     * @param jobName        任务/触发器 名称
     * @param jobGroupName   任务所在组名称
     * @param cronExpression cron表达式
     */
    public void addJob(Class jobClass, String jobName, String jobGroupName, String cronExpression) throws Exception {
        addJob(jobClass, jobName, jobGroupName,null, cronExpression, null);
    }

    /**
     * 创建job
     *
     * @param jobClass       任务/触发器 类
     * @param jobName        任务/触发器 名称
     * @param jobGroupName   任务所在组名称
     * @param cronExpression cron表达式
     */
    public void addJob(Class jobClass, String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) throws Exception {
        addJob(jobClass, jobName, jobGroupName,null, cronExpression, argMap);
    }


    /**
     * 创建job，可传参
     *
     * @param jobClass       任务类
     * @param jobName        任务/触发器 名称
     * @param jobGroupName   任务/触发器 所在组名称
     * @param description    任务描述
     * @param cronExpression cron表达式
     * @param argMap         map形式参数
     */
    public void addJob(Class jobClass, String jobName, String jobGroupName, String description, String cronExpression, Map<String, Object> argMap) throws Exception {
        try {
            // 判断是否已有该任务，有则先删除
            deleteJob(jobName, jobGroupName);
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(jobClass)
                    .withIdentity(jobName, jobGroupName)
                    .withDescription(description)
                    .build();
            // 构建Trigger信息
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(jobName, jobGroupName)
                    .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                    .build();
            //获得JobDataMap，写入数据
            if (argMap != null) {
                jobDetail.getJobDataMap().putAll(argMap);
            }
            // 调度容器设置JobDetail和Trigger
            scheduler.scheduleJob(jobDetail, trigger);
            // 如果调度器关闭就启动
            if (scheduler.isShutdown()) {
                scheduler.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(jobName + ":加入定时任务失败");
            throw new Exception("加入定时任务失败");
        }
    }

    /**
     * 暂停job
     *
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 所在组名称
     */
    public void pauseJob(String jobName, String jobGroupName) {
        try {
            scheduler.pauseJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 恢复job
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 所在组名称
     */
    public void resumeJob(String jobName, String jobGroupName) {
        try {
            scheduler.resumeJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 触发/执行
     * @param jobName 任务名称
     * @param jobGroupName 任务
     */
    public void triggerJob(String jobName, String jobGroupName) {
        try {
            scheduler.triggerJob(JobKey.jobKey(jobName, jobGroupName));
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * job 更新,只更新频率
     * @param jobName        任务/触发器 名称
     * @param jobGroupName   任务/触发器 所在组名称
     * @param cronExpression cron表达式
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression) {
        updateJob(jobName, jobGroupName, cronExpression, null);
    }


    /**
     * job 更新,更新频率和参数
     *
     * @param jobName        任务/触发器 名称
     * @param jobGroupName   任务/触发器 所在组名称
     * @param cronExpression cron表达式
     * @param argMap         参数
     */
    public void updateJob(String jobName, String jobGroupName, String cronExpression, Map<String, Object> argMap) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //修改map
            if (argMap != null) {
                trigger.getJobDataMap().putAll(argMap);
            }
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * job 更新,只更新更新参数
     *
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 所在组名称
     * @param argMap       参数
     */
    public void updateJob(String jobName, String jobGroupName, Map<String, Object> argMap) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            //修改map
            trigger.getJobDataMap().putAll(argMap);
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * job 删除
     *
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 所在组名称
     */
    public void deleteJob(String jobName, String jobGroupName) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
            if (triggerKey != null) {
                // 停止触发器
                scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroupName));
                // 移除触发器
                scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroupName));
                // 删除定时任务
                scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 启动所有定时任务
     */
    public void startAllJobs() {
        try {
            scheduler.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有定时任务
     */
    public void shutdownAllJobs() {
        try {
            if (!scheduler.isShutdown()) {
                scheduler.shutdown();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取所有任务列表
     *
     * @return 返回任务map列表
     */
    public List<Map<String, Object>> getAllJob() {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        List<Map<String, Object>> jobList = new ArrayList<>();
        Set<JobKey> jobKeys;
        try {
            jobKeys = scheduler.getJobKeys(matcher);
            for (JobKey jobKey : jobKeys) {
                List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
                for (Trigger trigger : triggers) {
                    Map<String, Object> job = new HashMap<>(16, 0.75f);
                    job.put("jobName", jobKey.getName());
                    job.put("jobGroupName", jobKey.getGroup());
                    job.put("trigger", trigger.getKey());
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                    job.put("jobStatus", triggerState.name());
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        String cronExpression = cronTrigger.getCronExpression();
                        job.put("cronExpression", cronExpression);
                    }
                    jobList.add(job);
                }
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return jobList;
    }

    /**
     * 获取某个任务
     *
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 组
     * @return 返回任务map
     */
    public Map<String, Object> getJobInfo(String jobName, String jobGroupName) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
        Map<String, Object> job = new HashMap<>(16, 0.75f);
        try {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                return null;
            }
            job.put("jobName", jobName);
            job.put("jobGroupName", jobGroupName);
            job.put("trigger", trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.put("jobStatus", triggerState.name());
            String cronExpression = trigger.getCronExpression();
            job.put("cronExpression", cronExpression);
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return job;
    }

    /**
     * 是否存在某个任务
     *
     * @param jobName      任务/触发器 名称
     * @param jobGroupName 任务/触发器 组
     * @return 返回boolean值
     */
    public boolean flagJob(String jobName, String jobGroupName) {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroupName);
        try {
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                return false;
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
        return true;
    }
}
