package com.scm.contract.service.quartz;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scm.contract.model.quartz.QuartzJob;
import com.scm.contract.dao.quartz.QuartzJobDao;
import com.scm.contract.tools.HumpLineAdopter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
 * @Description: 定时任务在线管理
 * @Author: jeecg-boot
 * @Date: 2019-04-28
 * @Version: V1.1
 */
@Slf4j
@Service
public class QuartzJobService extends ServiceImpl<QuartzJobDao, QuartzJob> {
    @Autowired
    private Scheduler scheduler;

    @Autowired
    private QuartzJobDao quartzJobDao;

    @Value("${direwolf.quartz.isOpen}")
    private boolean isOpen;

    /**
     * 立即执行的任务分组
     */
    private static final String JOB_TEST_GROUP = "test_group";

    public List<QuartzJob> findByJobClassName(String jobClassName) {
        return baseMapper.findByJobClassName(jobClassName);
    }

    /**
     * 项目启动时，初始化定时器
     * 主要是防止手动修改数据库导致未同步到定时任务处理（注：不能手动修改数据库ID和任务组名，否则会导致脏数据）
     */
//    @PostConstruct
    public void init() {
        if (isOpen) {
            List<QuartzJob> quartzJobs = quartzJobDao.listByOpenJob(1,0);

            for (QuartzJob quartzJob : quartzJobs) {
                this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
                System.out.println("运行脚本(id:" + quartzJob.getId() + ",内容:" + quartzJob.getDescription() + ")");
            }
        }
    }


    /**
     * 保存&启动定时任务
     *
     * @param quartzJob cronExpression,description,jobClassName,status
     * @return
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean saveAndScheduleJob(QuartzJob quartzJob) {
        // DB设置修改
        quartzJob.setDelFlag(0);
        boolean success = this.save(quartzJob);
        if (success) {
            if (1 == quartzJob.getStatus()) {
                // 定时器添加
                this.schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
            }
        }
        return success;
    }

    /**
     * 恢复定时任务
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean resumeJob(QuartzJob quartzJob) {
        schedulerDelete(quartzJob.getId());
        schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
        quartzJob.setStatus(1);
        return this.updateById(quartzJob);
    }

    /**
     * 编辑&启停定时任务
     *
     * @throws SchedulerException
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean editAndScheduleJob(QuartzJob quartzJob) throws SchedulerException {
        if (1 == quartzJob.getStatus()) {
            schedulerDelete(quartzJob.getId());
            schedulerAdd(quartzJob.getId(), quartzJob.getJobClassName().trim(), quartzJob.getCronExpression().trim(), quartzJob.getParameter());
        } else {
            scheduler.pauseJob(JobKey.jobKey(quartzJob.getId()));
        }
        return this.updateById(quartzJob);
    }

    /**
     * 删除&停止删除定时任务
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean deleteAndStopJob(QuartzJob job) {
        schedulerDelete(job.getId());
        boolean ok = this.removeById(job.getId());
        return ok;
    }

    /**
     * 立即执行定时任务
     *
     * @param quartzJob
     * @throws Exception
     */
    public void execute(QuartzJob quartzJob) throws Exception {
        String jobName = quartzJob.getJobClassName().trim();
        Date startDate = new Date();
        String ymd = DateFormatUtils.format(startDate,"yyyyMMddHHmmss");
        String identity = jobName + ymd;
        //3秒后执行 只执行一次
        // update-begin--author:sunjianlei ---- date:20210511--- for：定时任务立即执行，延迟3秒改成0.1秒-------
        startDate.setTime(startDate.getTime() + 100L);
        // update-end--author:sunjianlei ---- date:20210511--- for：定时任务立即执行，延迟3秒改成0.1秒-------
        // 定义一个Trigger
        SimpleTrigger trigger = (SimpleTrigger) TriggerBuilder.newTrigger()
                .withIdentity(identity, JOB_TEST_GROUP)
                .startAt(startDate)
                .build();
        // 构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobName).getClass()).withIdentity(identity).usingJobData("parameter", quartzJob.getParameter()).build();
        // 将trigger和 jobDetail 加入这个调度
        scheduler.scheduleJob(jobDetail, trigger);
        // 启动scheduler
        scheduler.start();
    }

    /**
     * 暂停定时任务
     *
     * @param quartzJob
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public void pause(QuartzJob quartzJob) {
        schedulerDelete(quartzJob.getId());
        quartzJob.setStatus(0);
        this.updateById(quartzJob);
    }

    /**
     * 添加定时任务
     *
     * @param jobClassName
     * @param cronExpression
     * @param parameter
     */
    private void schedulerAdd(String id, String jobClassName, String cronExpression, String parameter) {
        try {
            // 启动调度器
            scheduler.start();

            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass()).withIdentity(id).usingJobData("parameter", parameter).build();

            // 表达式调度构建器(即任务执行的时间)
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(id).withSchedule(scheduleBuilder).build();

            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            throw new RuntimeException("创建定时任务失败");
        } catch (RuntimeException e) {
            throw new RuntimeException(e.getMessage());
        } catch (Exception e) {
            throw new RuntimeException("后台找不到该类名：" + jobClassName);
        }
    }

    /**
     * 删除定时任务
     *
     * @param id
     */
    private void schedulerDelete(String id) {
        try {
            scheduler.pauseTrigger(TriggerKey.triggerKey(id));
            scheduler.unscheduleJob(TriggerKey.triggerKey(id));
            scheduler.deleteJob(JobKey.jobKey(id));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException("删除定时任务失败");
        }
    }

    private static Job getClass(String classname) throws Exception {
        Class<?> class1 = Class.forName(classname);
        return (Job) class1.newInstance();
    }

    /**
     * 分页列表查询(可带条件)
     *
     * @param params    查询条件
     * @param pageIndex 起始页
     * @param pageSize  size
     * @return
     */
    public Page<QuartzJob> queryPageList(Integer pageIndex, Integer pageSize, Map<String, Object> params) {
        params.remove("pageIndex");
        params.remove("pageSize");
        Page<QuartzJob> page = new Page<>(pageIndex, pageSize);

        QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
        if (CollectionUtils.isNotEmpty(params)) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                queryWrapper.eq(HumpLineAdopter.humpToLine(entry.getKey()), entry.getValue());
            }
        }
        return baseMapper.selectPage(page, queryWrapper);
    }

    /**
     * 根据条件查询列表
     *
     * @param params 条件
     * @return
     */
    public List<QuartzJob> queryList(Map<String, Object> params) {
        QueryWrapper<QuartzJob> queryWrapper = new QueryWrapper<>();
        if (CollectionUtils.isNotEmpty(params)) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                queryWrapper.eq(HumpLineAdopter.humpToLine(entry.getKey()), entry.getValue());
            }
        }
        return list(queryWrapper);
    }
}
