package com.codvision.base.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codvision.base.entity.QuartzJob;
import com.codvision.base.mapper.QuartzJobMapper;
import com.codvision.base.service.IQuartzJobService;
import com.codvision.base.support.QuartzJobManager;
import com.codvision.commoncore.exception.BusinessException;
import com.codvision.webcore.bean.QueryPage;
import com.codvision.webcore.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * qrtz任务详情 服务实现类
 * </p>
 *
 * @author lingee
 * @since 2023-11-20
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob> implements IQuartzJobService {

    @Resource
    private Scheduler scheduler;

    @Resource
    private QuartzJobManager quartzJobManager;

    @Override
    public IPage<QuartzJob> getPageResult(QueryPage<QuartzJob> queryPage) {
        IPage<QuartzJob> page = queryPage.getPage();
        QuartzJob queryBean = queryPage.getCondition();
        List<QuartzJob> list = this.getBaseMapper().getQuartzJobList(page, queryBean.getJobName(), queryBean.getJobGroup());
        try {
            for (QuartzJob vo : list) {
                JobKey key = new JobKey(vo.getJobName(), vo.getJobGroup());
                // 获取定时存储参数(方法名)
                JobDetail jobDetail = scheduler.getJobDetail(key);
                if (ObjectUtil.isNotEmpty(jobDetail)) {
                    vo.setJobMethodName(jobDetail.getJobDataMap().getString("jobMethodName"));
                }
            }
        } catch (SchedulerException e) {
            throw new BusinessException(10000, "获取定时任务列表错误");
        }

        return PageUtil.convertPage(page, list);
    }

    @Override
    public QuartzJob addQuartzJob(QuartzJob quartzJob) {
        boolean flagJob = quartzJobManager.flagJob(quartzJob.getJobName(), quartzJob.getJobGroup());
        if (flagJob) {
            throw new BusinessException("该任务组中已存在该名称任务！");
        }
        Class<?> jobClass;
        try {
            jobClass = Class.forName(quartzJob.getJobClassName());
            jobClass.getMethod(quartzJob.getJobMethodName());

            jobClass.newInstance();
            Map<String, Object> map = new HashMap<>(16);
            map.put("jobMethodName", quartzJob.getJobMethodName());
            // 加入定时任务
            quartzJobManager.addJob(jobClass, quartzJob.getJobName(), quartzJob.getJobGroup(), quartzJob.getDescription(), quartzJob.getCronExpression(), map);

        } catch (NoSuchMethodException e) {
            throw new BusinessException("定时任务类中未找到该方法！");
        } catch (ClassNotFoundException e) {
            throw new BusinessException("定时任务类中未找到该类！");
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new BusinessException("加入定时任务失败！");
        }
        return quartzJob;
    }

    @Override
    public QuartzJob updateJob(QuartzJob quartzJob) {
        // 删除对应定时任务
        quartzJobManager.deleteJob(quartzJob.getOldJobName(), quartzJob.getOldJobGroup());
        return addQuartzJob(quartzJob);
    }

    @Override
    public void batchDeleteJob(List<QuartzJob> quartzJobs) {
        quartzJobs.forEach(quartzJob -> quartzJobManager.deleteJob(quartzJob.getJobName(), quartzJob.getJobGroup()));
    }

    @Override
    public List<String> getAllMethod(String className) {
        Class<?> jobClass;
        try {
            jobClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(10000, "获取执行类失败！");
        }
        List<String> list = new ArrayList<>();
        // 获取执行类中所有的方法不包括父类和接口
        for (Method method : jobClass.getDeclaredMethods()) {
            if (!"execute".equals(method.getName())) {
                // 过滤掉定时任务执行方法
                list.add(method.getName());
            }
        }
        return list;
    }
}
