package com.bonus.tcp.task.service;




import com.bonus.tcp.task.mapper.ScheduleMapper;
import com.bonus.tcp.task.entity.TaskVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

@Slf4j
@Service("scheduleService")
public class ScheduleServiceImpl implements ScheduleService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleServiceImpl.class);

    @Autowired
    private ScheduleMapper scheduleDao;

    private Scheduler scheduler = null;

    /**
     * 暴露接口，唯一提供可调用的接口
     */
    @Override
    public void proceedSchedule() {
        try {
            this.proceed();
        } catch (Exception ex) {
            log.error(ex.toString(),ex);
            LOGGER.error(ex.getMessage());
        }

    }

    //
    // 以下方法皆为 私有方法，请勿调用
    //

    private void proceed() throws Exception {
        List<TaskVo> list = scheduleDao.getAllTask();
        if(null == list || list.isEmpty()) {
            return;
        }
        TaskVo taskVo=new TaskVo();
        taskVo.setCorn("0/30 * * * * ?");
        taskVo.setJobClass("com.bonus.tcp.task.config.ConfigScanJob");
        taskVo.setTaskCode("DEFAULT");
        taskVo.setCode("1");

        list.forEach(vo->{
            vo.setJobClass("com.bonus.tcp.task.job.TestJob");
            vo.setTaskCode("TASK_"+vo.getCode());
        });
        list.add(taskVo);
        Set<String> currentScheduleCodeSet = this.getCurrentScheduleCodeSet();

        this.proceed(list, currentScheduleCodeSet);
    }


    /**
     * 细节调用
     * @param list
     * @param currentScheduleCodeSet
     * @throws Exception
     */
    private void proceed(List<TaskVo> list, Set<String> currentScheduleCodeSet) throws Exception {
        for(TaskVo task : list) {
            if(null == task) {
                continue;
            }
            if(!this.isTaskCodeExist(task.getCode(), currentScheduleCodeSet)){//未在进程中
                this.startTask(task);
            }else{//已存在
                this.updateJobTime(task);
            }
        }
        for (String str : currentScheduleCodeSet) {
            System.err.println(str);
            Boolean isCz=true;
            for (TaskVo vo:list){
                if(vo.getCode().equals(str)){
                    isCz=false;
                }
            }
            if(isCz){
                TaskVo taskVo=new TaskVo();
                taskVo.setJobClass("com.bonus.tcp.task.job.TestJob");
                taskVo.setTaskCode("TASK_"+str);
                taskVo.setCode(str);
                stopTask(taskVo);
            }
        }
    }

    /**
     * 更新当前正在运行的任务的cron时间
     * @param
     * @param
     */
    private  void updateJobTime(TaskVo taskVo){
        try{
            if(!this.validate(taskVo)) {
                return;
            }
            //build触发key
            ScheduleNaming naming = new ScheduleNaming(taskVo);
            TriggerKey triggerKey =TriggerKey.triggerKey(naming.getTriggerName(), Scheduler.DEFAULT_GROUP);
            CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            String oldTime = cronTrigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(taskVo.getCorn())) {
                //   log.info("=====刷新定时器时间====");
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(taskVo.getCorn());
                CronTrigger trigger = TriggerBuilder.newTrigger()
                        .usingJobData("taskId",taskVo.getCode())
                        .withIdentity(naming.getTriggerName(), Scheduler.DEFAULT_GROUP)
                        .withSchedule(cronScheduleBuilder).build();
                //重置对应的job
                scheduler.rescheduleJob(triggerKey, trigger);
            }

        }catch (Exception e){

        }

    }
    /**
     * 启用任务
     * @param scheduleTask
     * @throws Exception
     */
    private void startTask(TaskVo scheduleTask) throws Exception {
        if(!this.validate(scheduleTask)) {
            return;
        }

        ScheduleNaming naming = new ScheduleNaming(scheduleTask);
        Class<? extends Job> clazz = this.getJobClass(naming.getClassName());

        JobDetail jobDetail = this.getJobDetail(clazz, naming.getJobName(),naming.getTaskId());
        Trigger trigger = this.getTrigger(naming.getTriggerName(), naming.getScheduleConfiguration(),naming.getTaskId());

        this.scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 停用任务
     * @param task
     * @throws Exception
     */
    private void stopTask(TaskVo task) throws Exception {

        if(!this.validate(task)) {
            return;
        }

        ScheduleNaming naming = new ScheduleNaming(task);

        this.scheduler.unscheduleJob(TriggerKey.triggerKey(naming.getTriggerName(), Scheduler.DEFAULT_GROUP));
        this.scheduler.deleteJob(JobKey.jobKey(naming.getJobName(), Scheduler.DEFAULT_GROUP));
    }


    /**
     * 验证参数
     * @param schedule
     * @return
     */
    private boolean validate(TaskVo schedule) {
        if(null == schedule) {
            return false;
        } else if (null == schedule.getCode()) {
            return false;
        }


        return true;
    }

    /**
     * 必须返回 继承 Job 的 Class
     * @param className
     * @return
     * @throws ClassNotFoundException
     */
    @SuppressWarnings("unchecked")
    private Class<? extends Job> getJobClass(String className) throws ClassNotFoundException {
        return (Class<? extends Job>) Class.forName(className);
    }


    /**
     * 获取 jobDetail
     * @param clazz
     * @param jobName
     * @return
     */
    private JobDetail getJobDetail(Class<? extends Job> clazz, String jobName,String taskId) {
        JobDetail jobDetail = newJob(clazz)
                .withIdentity(jobName, Scheduler.DEFAULT_GROUP)
                .usingJobData("taskId",taskId)
                .build()
                ;

        return jobDetail;
    }


    /**
     * 获取 trigger
     * @param triggerName
     * @param conf
     * @return
     */
    private Trigger getTrigger(String triggerName, String conf,String taskId) {
        CronTrigger trigger = newTrigger()
                .withIdentity(triggerName, Scheduler.DEFAULT_GROUP)
                .usingJobData("taskId",taskId)
                .withSchedule(CronScheduleBuilder.cronSchedule(conf))
                .startNow()
                .build();

        return trigger;
    }



    private boolean isTaskCodeExist(String taskCode, Set<String> set) {

        return set.contains(taskCode);
    }


    private Set<String> getCurrentScheduleCodeSet() throws Exception{

        Set<JobKey> jobKeySet = scheduler.getJobKeys(GroupMatcher.jobGroupEquals(Scheduler.DEFAULT_GROUP));
        Set<String> scheduleCodeSet = new HashSet<String>();
        for(JobKey jobKey : jobKeySet) {
            scheduleCodeSet.add(jobKey.getName());
        }

        return scheduleCodeSet;

    }


    /**
     * 内部类，用来统一定义名称
     */
    class ScheduleNaming {
        private String taskCode = "taskCode";
        private String className;
        private String triggerName;
        private String jobName;
        private String scheduleConfiguration;
        private String taskId;

        ScheduleNaming(TaskVo task) {
            if(null != task.getCode()) {
                this.taskCode = task.getCode();
            }
            this.className = task.getJobClass();
            this.triggerName = task.getTaskCode();
            this.jobName = this.taskCode;
            this.taskId=task.getCode();
            this.scheduleConfiguration = task.getCorn();
        }

        public String getClassName() {
            return className;
        }

        public String getTriggerName() {
            return triggerName;
        }

        public String getJobName() {
            return jobName;
        }

        public String getScheduleConfiguration() {
            return scheduleConfiguration;
        }

        public String getTaskCode() {
            return taskCode;
        }

        public String getTaskId() {
            return taskId;
        }
    }


    @Override
    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }



}