package com.yice.common.quartz.service;

import cn.hutool.core.map.MapUtil;
import com.yice.common.quartz.object.QuartzJobData;
import com.yice.common.quartz.object.QuartzJobParam;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * Quartz Job 数据操作接口。
 *
 * @author linking
 * @date 2023-04-13
 */
@Service
public class QuartzJobService {

    @Autowired
    private Scheduler scheduler;

    /**
     * 保存新Job。
     *
     * @param jobParam Job的参数对象。
     */
    @SuppressWarnings("unchecked")
    @Transactional
    public void saveNew(QuartzJobParam jobParam) throws ClassNotFoundException, SchedulerException {
        Class<? extends QuartzJobBean> clazz =
                (Class<? extends QuartzJobBean>) Class.forName(jobParam.getJobClassName());
        // 构建job信息
        JobDetail job = JobBuilder.newJob(clazz)
                .withIdentity(jobParam.getJobName(), jobParam.getJobGroup())
                .withDescription(jobParam.getDescription())
                .storeDurably().build();
        if (MapUtil.isNotEmpty(jobParam.getJobDataMap())) {
            job.getJobDataMap().putAll(jobParam.getJobDataMap());
        }
        // 触发时间点
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(jobParam.getJobName(), jobParam.getJobGroup())
                .withSchedule(CronScheduleBuilder.cronSchedule(jobParam.getCronExpression()))
                .build();
        // 交由Scheduler安排触发
        scheduler.scheduleJob(job, trigger);
    }

    /**
     * 删除Job。
     *
     * @param jobName  Job名称。
     * @param jobGroup Job组名。
     */
    public void remove(String jobName, String jobGroup) throws SchedulerException {
        List<? extends Trigger> triggers = scheduler.getTriggersOfJob(JobKey.jobKey(jobName, jobGroup));
        if (triggers.size() > 0) {
            if (!"PAUSED".equals(scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup)).name())) {
                scheduler.pauseTrigger(TriggerKey.triggerKey(jobName, jobGroup));
            }
            scheduler.unscheduleJob(TriggerKey.triggerKey(jobName, jobGroup));
        }
        scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
    }

    /**
     * 获取当前Scheduler的所有Job数据列表。
     *
     * @return 当前Scheduler的所有Job数据列表。
     */
    public List<QuartzJobData> getAllJobDataList() throws SchedulerException {
        List<QuartzJobData> jobInfos = new LinkedList<>();
        List<String> groups = scheduler.getJobGroupNames();
        for (String group : groups) {
            GroupMatcher<JobKey> groupMatcher = GroupMatcher.groupEquals(group);
            Set<JobKey> jobKeys = scheduler.getJobKeys(groupMatcher);
            for (JobKey jobKey : jobKeys) {
                QuartzJobData jobData = new QuartzJobData();
                JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                jobData.setJobName(jobKey.getName());
                jobData.setJobGroup(jobKey.getGroup());
                jobData.setJobClassName(jobDetail.getJobClass().getName());
                jobData.setJobDataMap(jobDetail.getJobDataMap());
                Trigger jobTrigger = scheduler.getTrigger(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                if (jobTrigger != null) {
                    Trigger.TriggerState state =
                            scheduler.getTriggerState(TriggerKey.triggerKey(jobKey.getName(), jobKey.getGroup()));
                    jobData.setTriggerName(jobKey.getName());
                    jobData.setTriggerGroup(jobKey.getGroup());
                    CronTrigger cronTrigger = (CronTrigger) jobTrigger;
                    jobData.setCronExpression(cronTrigger.getCronExpression());
                    jobData.setDescription(jobDetail.getDescription());
                    jobData.setState(state.name());
                } else {
                    jobData.setState("OVER");
                }
                jobInfos.add(jobData);
            }
        }
        return jobInfos;
    }

    /**
     * 触发Job。
     *
     * @param jobName        Job名称。
     * @param jobGroup       Job组名。
     */
    public void trigger(String jobName, String jobGroup) throws SchedulerException {
        scheduler.triggerJob(JobKey.jobKey(jobName, jobGroup));
    }

    /**
     * 重新调度Job。
     *
     * @param jobName        Job名称。
     * @param jobGroup       Job组名。
     * @param cronExpression 新的Cron表达式。
     */
    public void reschedule(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
        Trigger.TriggerState triggerState =
                scheduler.getTriggerState(TriggerKey.triggerKey(jobName, jobGroup));
        CronTrigger cronTrigger =
                (CronTrigger) scheduler.getTrigger(TriggerKey.triggerKey(jobName, jobGroup));
        if (!cronTrigger.getCronExpression().equals(cronExpression)) {
            CronTrigger newCronTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(jobName, jobGroup)
                    .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                    .build();
            scheduler.rescheduleJob(TriggerKey.triggerKey(jobName, jobGroup), newCronTrigger);
            if ("PAUSED".equals(triggerState.name())) {
                this.pause(jobName, jobGroup);
            }
        }
    }

    /**
     * 暂定Job。
     *
     * @param jobName        Job名称。
     * @param jobGroup       Job组名。
     */
    public void resume(String jobName, String jobGroup) throws SchedulerException {
        scheduler.resumeJob(JobKey.jobKey(jobName, jobGroup));
    }

    /**
     * 恢复暂定的Job。
     *
     * @param jobName        Job名称。
     * @param jobGroup       Job组名。
     */
    public void pause(String jobName, String jobGroup) throws SchedulerException {
        scheduler.pauseJob(JobKey.jobKey(jobName, jobGroup));
    }
}
