package com.ozs.da.service.job.impl;

import com.ozs.da.constant.Constant;
import com.ozs.da.entity.task.Task;
import com.ozs.da.service.job.TimeTriggerJobListener;
import com.ozs.da.utils.CronExpresUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.text.ParseException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author 许明
 * on 2019/3/7.
 * @version 2.0.0
 * 时间触发调度
 */
public class SchedulerTimeTriggeJob {

    private static final Logger logger = LogManager.getLogger(SchedulerTimeTriggeJob.class);

    private static final String cronTriggers = "CRONTRIGGER";
    private static final String JOB_KEY = "jobid";

    public static Scheduler scheduler = null;

    /**
     * @return
     * @description 非周期job初始化线程池
     */
    public static int initScheduler() {
        synchronized (SchedulerTimeTriggeJob.class) {
            try {
                if (null == scheduler) {
                    scheduler = StdSchedulerFactory.getDefaultScheduler();
                    scheduler.getListenerManager().addJobListener(new TimeTriggerJobListener());
                    logger.info("创建Scheduler实例成功.........");
                }
            } catch (Exception e) {
                scheduler = null;
                logger.error("创建Scheduler实例异常............", e);
                return Constant.RETURN_FAILD;
            }
            return Constant.RETURN_OK;
        }
    }

    public static void start() throws SchedulerException {
        scheduler.start();
    }

    /**
     * @throws SchedulerException
     * @description TODO
     */
    public static void standby() throws SchedulerException {
        scheduler.standby();
    }

    public static void clear() throws SchedulerException {
        scheduler.clear();
    }

    public static void putMemJobSchedule(List<Task> taskList) throws SchedulerException, ParseException {
        for(Task task : taskList){
            scheduleFixedJob(scheduler, task, true);
        }
    }

    /**
     * @param scheduler
     * @param timetriggerjob
     * @param isReplaceIfExist 是否覆盖quartz中已经存在的同名job
     * @throws SchedulerException
     * @throws ParseException
     * @description 装配时间触发调度事件
     */
    private static void scheduleFixedJob(Scheduler scheduler, Task timetriggerjob, boolean isReplaceIfExist)
            throws SchedulerException, ParseException {
        Date current = new Date();
        // 配置Job
        String jobid = timetriggerjob.getId();
        JobKey jobKey = new JobKey(jobid);
        JobDetail job = JobBuilder.newJob(TimeTriggerJob.class).withIdentity(jobKey).build();

        job.getJobDataMap().put(JOB_KEY, timetriggerjob.getId());
        // 配置时间触发器,可以为每个job配置多个触发器
        Set<CronTrigger> triggers = new HashSet<CronTrigger>();
        CronTrigger trigger = null;
        String taskCron = timetriggerjob.getTaskCron();
        //获取符合cron表达式的下次执行时间
        Date nextFiredTime = getNextFiredDate(taskCron, current);
        trigger = TriggerBuilder.newTrigger().withIdentity(jobid).withSchedule(CronScheduleBuilder.cronSchedule(taskCron)).startAt(nextFiredTime).build();
        triggers.add(trigger);
        if (CronExpresUtil.getNextFiredTime(triggers, current).getTime() - current.getTime() < Constant.THREAD_SCANJOB_INTERVAL) {
            job.getJobDataMap().put(cronTriggers, triggers);
            scheduler.scheduleJob(job, triggers, isReplaceIfExist);
        }
    }

    /**
     * @param task
     * @description 时间触发调度，将job添加到quartz中去
     */
    public static void scheduler(Task task) {
        String taskCron = task.getTaskCron();
        String jobid = task.getId();
        if (null == taskCron || taskCron.isEmpty()) {
            logger.warn("quartz任务触发异常[" + jobid + "]对应CRON为空，JOB不执行");
            return;
        }
        try {
            scheduler(task, taskCron, true);
        } catch (SchedulerException e) {
            logger.error("quartz任务触发异常[" + jobid + "]", e);
        } catch (ParseException e) {
            logger.error("quartz任务触发异常[" + jobid + "]配置错误,解析异常", e);
        }
    }

    /**
     * @param job
     * @param taskCron
     * @throws SchedulerException
     * @throws ParseException
     * @description scheduler重载方法，提供job以及对应的cron表达式
     */
    public static void scheduler(Task job, String taskCron, boolean isReplaceIfExist) throws SchedulerException, ParseException {
        String jobid = job.getId();
        if (!taskCron.isEmpty()) {
            // 如果job被修改，则quartz覆盖scheduler中已经存在的scheduler job
            logger.info("quartz任务job key[" + jobid + "].........");
            if (scheduler.getJobDetail(new JobKey(jobid)) == null || isReplaceIfExist) {
                scheduleFixedJob(scheduler, job, taskCron, isReplaceIfExist);
            }
        }
    }

    /**
     * @param scheduler
     * @param timetriggerjob
     * @param taskCron
     * @param isReplaceIfExist 是否覆盖quartz中已经存在的同名job
     * @throws SchedulerException
     * @throws ParseException
     * @description 装配时间触发调度事件
     */
    private static void scheduleFixedJob(Scheduler scheduler, Task timetriggerjob, String taskCron, boolean isReplaceIfExist)
            throws SchedulerException, ParseException {
        Date current = new Date();
        // 配置Job
        String jobid = timetriggerjob.getId();
        JobKey jobKey = new JobKey(jobid);
        JobDetail job = JobBuilder.newJob(TimeTriggerJob.class).withIdentity(jobKey).build();

        job.getJobDataMap().put(JOB_KEY, timetriggerjob.getId());
        // 配置时间触发器,可以为每个job配置多个触发器
        Set<CronTrigger> triggers = new HashSet<CronTrigger>();
        CronTrigger trigger = null;
        //获取符合cron表达式的下次执行时间
        Date nextFiredTime = getNextFiredDate(taskCron, current);
        trigger = TriggerBuilder.newTrigger().withIdentity(jobid).withSchedule(CronScheduleBuilder.cronSchedule(taskCron)).startAt(nextFiredTime).build();
        triggers.add(trigger);
        if (CronExpresUtil.getNextFiredTime(triggers, current).getTime() - current.getTime() < Constant.THREAD_SCANJOB_INTERVAL) {
            job.getJobDataMap().put(cronTriggers, triggers);
            scheduler.scheduleJob(job, triggers, isReplaceIfExist);
        }
    }

    public static Date getNextFiredDate(String taskCron, Date date) throws ParseException {
        Date result = null;
        Date nextFiretime = null;
        CronExpression cron = new CronExpression(taskCron);
        nextFiretime = cron.getTimeAfter(date);
        if (null == nextFiretime) {
            return result;
        }
        if (null != nextFiretime) {
            result = (null == result ? nextFiretime : (nextFiretime.before(result) ? nextFiretime : result));
        }
        return result;
    }

    /**
     *
     * @description 根据job id 删除相应的job
     * @param task
     * @return
     */
    public static void deleteJobByJobId(Task task)
    {
        try
        {
            if (null != scheduler)
            {
                scheduler.deleteJob(new JobKey(task.getId()));
            }
        }
        catch (SchedulerException e)
        {
            logger.error("删除JOB异常，JOB_ID[" + task.getId() + "]", e);
        }
    }

    /**
     * 停止job
     * @param task
     */
    public static void shutDownJobByJobid(Task task)
    {
        try
        {
            scheduler.deleteJob(new JobKey(task.getId()));
        }
        catch (SchedulerException e)
        {
            logger.error("停止quartz中的job失败[" + task.getId() + "]", e);
        }
    }

    /**
     *
     * @description 更新时间触发job相关配置信息
     * @param timetriggerjob
     *            job
     * @param taskCcron
     *            job对应cron表达式
     * @return
     */
    public static void updateJobByJobid(Task timetriggerjob, String taskCcron)
    {
        try
        {
            if (scheduler.checkExists(new JobKey(timetriggerjob.getId())))
            {
                // 如果更新的Job存在
                scheduler(timetriggerjob, taskCcron, true);
            }
            else
            {
                // 如果不存在
                scheduler(timetriggerjob);
            }
        }
        catch (SchedulerException e)
        {
            logger.error("Job__" + timetriggerjob.getId() + "__任务更新失败,SchedulerException happened ", e);
        }
        catch (ParseException e)
        {
            logger.error("Job__" + timetriggerjob.getId() + "__任务更新失败,ParseException happened ", e);
        }
    }
}
