package com.jwipc.job.logic.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.jwipc.job.logic.JobServiceLogic;
import com.jwipc.job.prop.JobProp;
import com.jwipc.job.util.JobUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.StdScheduler;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MINUTE_FORMATTER;
import static org.quartz.SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class JobServiceLogicImpl implements JobServiceLogic {
    private static final String TOA_JOB_GROUP = "ToaJobGroup";
    @Autowired
    @Lazy
    private SchedulerFactoryBean schedulerFactoryBean;
    @Autowired
    private ApplicationContext context;
    @Autowired
    private JobProp jobProp;

    @Override
    public boolean scheduleCronJob(String jobName,
                                   Class<? extends QuartzJobBean> jobClass,
                                   Date date,
                                   String cronExpression, String remark)
    {
        final JobDetail jobDetail;
        if (StrUtil.isNotEmpty(remark)) {
            jobDetail = JobUtil.createJob(jobClass, false, context, jobName, TOA_JOB_GROUP, remark);
        } else {
            jobDetail = JobUtil.createJob(jobClass, false, context, jobName, TOA_JOB_GROUP);
        }


        if (log.isDebugEnabled()) {
            log.debug("创建一个名称为 {} 并且在 {} 开始执行的定时任务", jobName, date);
        }
        Trigger cronTriggerBean = JobUtil.createCronTrigger(jobName, date, cronExpression,
                MISFIRE_INSTRUCTION_FIRE_NOW);

        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            Date dt = scheduler.scheduleJob(jobDetail, cronTriggerBean);

            log.info("Job with key jobKey :{} and group :{} scheduled successfully for date : {}", jobName,
                    TOA_JOB_GROUP, dt);
            return true;
        } catch (SchedulerException e) {
            log.error("SchedulerException while scheduling job with key :{} ", jobName, e);
        }

        return false;
    }


    @Override
    public boolean updateOneTimeJob(String jobName, Date date, String remark) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for updating one time job : jobKey :{}, date: {}", jobName, date);
        }
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            final JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, TOA_JOB_GROUP));
            final JobDataMap jobDataMap = jobDetail.getJobDataMap();
            if (jobDataMap != null) {
                jobDataMap.put("remark", remark);
            }
            scheduler.addJob(jobDetail, true);
            Trigger newTrigger = JobUtil.createSingleTrigger(jobName, date, MISFIRE_INSTRUCTION_FIRE_NOW);
            final TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
            Date dt = schedulerFactoryBean.getScheduler().rescheduleJob(triggerKey, newTrigger);
            log.info("Trigger associated with jobKey :{} rescheduled successfully for date :{}", jobName, dt);
            return true;
        } catch (Exception e) {
            log.error("SchedulerException while updating one time job with key :{} ", jobName, e);
        }
        return false;
    }


    @Override
    public boolean updateCronJob(String jobName, Date date, String cronExpression, String remark) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for updating cron job,jobKey :{}, date: {}", jobName, date);
        }
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            final JobDetailImpl jobDetail = (JobDetailImpl) scheduler.getJobDetail(
                    JobKey.jobKey(jobName, TOA_JOB_GROUP));
            final JobDataMap jobDataMap = jobDetail.getJobDataMap();
            if (jobDataMap != null) {
                jobDataMap.put("remark", remark);
            }
            jobDetail.setDurability(true);
            scheduler.addJob(jobDetail, true);
            Trigger newTrigger;
            newTrigger = JobUtil.createCronTrigger(jobName, date, cronExpression, MISFIRE_INSTRUCTION_FIRE_NOW);
            final TriggerKey triggerKey = TriggerKey.triggerKey(jobName);
            Date dt = schedulerFactoryBean.getScheduler().rescheduleJob(triggerKey, newTrigger);
            log.info("Trigger associated with jobKey:{} rescheduled successfully for date :{}",
                    jobName, dt);
            scheduler.addJob(jobDetail, true);
            return true;
        } catch (Exception e) {
            log.error("SchedulerException while updating cron job with key :{} ", jobName, e);
            return false;
        }
    }


    @Override
    public boolean unScheduleJob(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for unscheduling job: {}", jobName);
        }
        TriggerKey tkey = new TriggerKey(jobName);
        try {
            boolean status = schedulerFactoryBean.getScheduler().unscheduleJob(tkey);

            log.info("Trigger associated with jobKey:{} nscheduled with status:{}",
                    jobName, status);
            return status;
        } catch (SchedulerException e) {
            log.error("SchedulerException while unscheduling job with key :{} ", jobName, e);
            return false;
        }
    }


    @Override
    public boolean deleteJob(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for deleting job : jobKey is {}", jobName);
        }

        JobKey jkey = new JobKey(jobName, TOA_JOB_GROUP);

        try {
            boolean status = schedulerFactoryBean.getScheduler().deleteJob(jkey);
            log.info("Job with jobKey:{} deleted with status:{}",
                    jobName, status);
            return status;
        } catch (SchedulerException e) {
            log.error("SchedulerException while deleting job with key :{} ", jobName, e);
            return false;
        }
    }

    @Override
    public boolean pauseJob(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for pausing job : jobKey： {}, groupKey :{}", jobName, TOA_JOB_GROUP);
        }
        JobKey jkey = new JobKey(jobName, TOA_JOB_GROUP);
        try {
            schedulerFactoryBean.getScheduler().pauseJob(jkey);
            log.info("Job with jobKey:{} paused succesfully.", jobName);
            return true;
        } catch (SchedulerException e) {
            log.error("SchedulerException while pausing job with key :{} ", jobName, e);
            return false;
        }
    }

    @Override
    public boolean resumeJob(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for resuming job : jobKey： {}, groupKey :{}", jobName, TOA_JOB_GROUP);
        }

        JobKey jKey = new JobKey(jobName, TOA_JOB_GROUP);
        try {
            schedulerFactoryBean.getScheduler().resumeJob(jKey);
            log.info("Job with jobKey:{} resumed succesfully.", jobName);
            return true;
        } catch (SchedulerException e) {
            log.error("SchedulerException while resuming job with key :{} ", jobName, e);
            return false;
        }
    }

    @Override
    public boolean startJobNow(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for starting job now : jobKey： {}, groupKey :{}", jobName, TOA_JOB_GROUP);
        }

        JobKey jKey = new JobKey(jobName, TOA_JOB_GROUP);
        try {
            final StdScheduler scheduler = (StdScheduler) schedulerFactoryBean.getScheduler();
            scheduler.triggerJob(jKey);
            log.info("Job with jobKey:{} started now succesfully.", jobName);
            return true;
        } catch (SchedulerException e) {
            log.error("SchedulerException while starting job with key :{} ", jobName, e);
            return false;
        }
    }

    @Override
    public boolean isJobRunning(String jobName) {
        if (log.isDebugEnabled()) {
            log.debug("Parameters received for checking is running now. jobKey： {}, groupKey :{}", jobName,
                    TOA_JOB_GROUP);
        }
        try {
            List<JobExecutionContext> currentJobs = schedulerFactoryBean.getScheduler().getCurrentlyExecutingJobs();
            if (currentJobs != null) {
                for (JobExecutionContext jobCtx : currentJobs) {
                    final JobDetail jobDetail = jobCtx.getJobDetail();
                    final JobKey jobKey = jobDetail.getKey();
                    String jobNameDB = jobKey.getName();
                    String groupNameDB = jobKey.getGroup();

                    if (StrUtil.equalsIgnoreCase(jobNameDB, jobName)
                            && StrUtil.equalsIgnoreCase(groupNameDB, TOA_JOB_GROUP)) {
                        return true;
                    }
                }
            }
        } catch (SchedulerException e) {
            log.error("SchedulerException while checking job with key :{} is running.", jobName, e);
        }
        return false;
    }

    @Override
    public List<Map<String, String>> getAllJobs() {
        List<Map<String, String>> list = CollUtil.newArrayList();
        try {
            Scheduler scheduler = schedulerFactoryBean.getScheduler();

            for (String groupName : scheduler.getJobGroupNames()) {
                final GroupMatcher<JobKey> keyGroupMatcher = GroupMatcher.jobGroupEquals(groupName);
                final Set<JobKey> jobKeys = scheduler.getJobKeys(keyGroupMatcher);
                for (JobKey jobKey : jobKeys) {

                    String jobName = jobKey.getName();
                    String jobGroup = jobKey.getGroup();

                    final JobDetail jobDetail = scheduler.getJobDetail(jobKey);
                    final JobDataMap jobDataMap = jobDetail.getJobDataMap();

                    String remark = StrUtil.EMPTY;
                    if (jobDataMap != null) {
                        remark = jobDataMap.getString("remark");
                    }

                    //get job's trigger
                    final List<? extends Trigger> triggersOfJob = scheduler.getTriggersOfJob(jobKey);
                    final Trigger trigger = triggersOfJob.get(0);
                    String cronEx = StrUtil.EMPTY;
                    if (trigger instanceof CronTrigger) {
                        CronTrigger cronTrigger = (CronTrigger) trigger;
                        cronEx = cronTrigger.getCronExpression();
                    }

                    Date scheduleTime = trigger.getStartTime();
                    Date nextFireTime = trigger.getNextFireTime();
                    Date lastFiredTime = trigger.getPreviousFireTime();

                    Map<String, String> map = MapUtil.newHashMap();
                    map.put("jobName", jobName);
                    map.put("jobClass", jobDetail.getJobClass().getSimpleName());
                    map.put("groupName", jobGroup);
                    map.put("scheduleTime", DateUtil.format(scheduleTime, NORM_DATETIME_MINUTE_FORMATTER));
                    map.put("lastFiredTime", DateUtil.formatDateTime(lastFiredTime));
                    map.put("nextFireTime", DateUtil.formatDateTime(nextFireTime));
                    map.put("cronEx", cronEx);
                    map.put("remark", remark);

                    if (isJobRunning(jobName)) {
                        map.put("jobStatus", "RUNNING");
                    } else {
                        String jobState = getJobState(jobName);
                        map.put("jobStatus", jobState);
                    }

                    list.add(map);

                    if (log.isDebugEnabled()) {
                        log.debug("Job details: Name: {} , Group Name:{}, Schedule Time: {}",
                                jobName, groupName, scheduleTime);
                    }
                }

            }
        } catch (SchedulerException e) {
            log.error("SchedulerException while fetching all jobs.", e);
        }
        return list;
    }

    @Override
    public boolean isJobWithNamePresent(String jobName) {
        try {
            JobKey jobKey = new JobKey(jobName, TOA_JOB_GROUP);
            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            if (scheduler.checkExists(jobKey)) {
                return true;
            }
        } catch (SchedulerException e) {
            log.error("SchedulerException while checking job with name and group exist.", e);
        }
        return false;
    }

    @Override
    public String getJobState(String jobName) {


        try {
            JobKey jobKey = new JobKey(jobName, TOA_JOB_GROUP);

            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobDetail jobDetail = scheduler.getJobDetail(jobKey);

            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobDetail.getKey());
            if (triggers != null && triggers.size() > 0) {
                for (Trigger trigger : triggers) {
                    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());

                    if (Trigger.TriggerState.PAUSED.equals(triggerState)) {
                        return "PAUSED";
                    } else if (Trigger.TriggerState.BLOCKED.equals(triggerState)) {
                        return "BLOCKED";
                    } else if (Trigger.TriggerState.COMPLETE.equals(triggerState)) {
                        return "COMPLETE";
                    } else if (Trigger.TriggerState.ERROR.equals(triggerState)) {
                        return "ERROR";
                    } else if (Trigger.TriggerState.NONE.equals(triggerState)) {
                        return "NONE";
                    } else if (Trigger.TriggerState.NORMAL.equals(triggerState)) {
                        return "SCHEDULED";
                    }
                }
            }
        } catch (SchedulerException e) {
            log.error("SchedulerException while checking job with name and group exist.", e);
        }
        return null;
    }

    @Override
    public boolean stopJob(String jobName) {
        try {

            Scheduler scheduler = schedulerFactoryBean.getScheduler();
            JobKey jkey = new JobKey(jobName, TOA_JOB_GROUP);

            return scheduler.interrupt(jkey);

        } catch (SchedulerException e) {
            log.error("SchedulerException while stopping job. ", e);
        }
        return false;
    }
}