package org.nl.system.service.quartz.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.nl.common.domain.query.PageQuery;
import org.nl.common.exception.BadRequestException;
import org.nl.common.utils.RedisUtils;
import org.nl.common.utils.SecurityUtils;
import org.nl.system.service.quartz.ISysQuartzJobService;
import org.nl.system.service.quartz.dao.SysQuartzJob;
import org.nl.system.service.quartz.dao.SysQuartzLog;
import org.nl.system.service.quartz.dao.mapper.SysQuartzJobMapper;
import org.nl.system.service.quartz.dao.mapper.SysQuartzLogMapper;
import org.nl.system.service.quartz.dto.JobQuery;
import org.nl.system.service.quartz.utils.QuartzManage;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 定时任务表 服务实现类
 * </p>
 *
 * @author generator
 * @since 2022-12-19
 */
@Service
public class SysQuartzJobServiceImpl extends ServiceImpl<SysQuartzJobMapper, SysQuartzJob> implements ISysQuartzJobService {
    @Autowired
    private SysQuartzJobMapper quartzJobMapper;
    @Autowired
    private SysQuartzLogMapper quartzLogMapper;
    @Autowired
    private QuartzManage quartzManage;
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public IPage<SysQuartzJob> queryAll(JobQuery criteria, PageQuery pageable) {
        return this.page(pageable.build(SysQuartzJob.class), criteria.build());
    }

    @Override
    public IPage<SysQuartzLog> queryAllLog(JobQuery criteria, PageQuery page) {
        LambdaQueryWrapper<SysQuartzLog> lam = new LambdaQueryWrapper<>();
        lam.like(ObjectUtil.isNotEmpty(criteria.getJob_name()), SysQuartzLog::getJob_name, criteria.getJob_name())
                .eq(ObjectUtil.isNotEmpty(criteria.getIs_success()), SysQuartzLog::getIs_success, criteria.getIs_success())
                .nested(ObjectUtil.isNotEmpty(criteria.getCreate_time()), i -> {
                    i.ge(SysQuartzLog::getCreate_time, criteria.getCreate_time().get(0))
                     .le(SysQuartzLog::getCreate_time, criteria.getCreate_time().get(1));
                });
        IPage<SysQuartzLog> pages = new Page<>(page.getPage() + 1, page.getSize());
        quartzLogMapper.selectPage(pages, lam);
        return pages;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createJob(SysQuartzJob resources) {
        if (!CronExpression.isValidExpression(resources.getCron_expression())) {
            throw new BadRequestException("cron表达式格式错误");
        }
        String currentUserId = SecurityUtils.getCurrentUserId();
        String nickName = SecurityUtils.getCurrentNickName();
        String now = DateUtil.now();
        resources.setJob_id(IdUtil.getSnowflake(1,1).nextIdStr());
        resources.setCreate_id(currentUserId);
        resources.setCreate_name(nickName);
        resources.setCreate_time(now);
        resources.setUpdate_id(currentUserId);
        resources.setUpdate_name(nickName);
        resources.setUpdate_time(now);
        quartzJobMapper.insert(resources);
        // 添加到任务管理
        quartzManage.addJob(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateJob(SysQuartzJob resources) {
        if (!CronExpression.isValidExpression(resources.getCron_expression())) {
            throw new BadRequestException("cron表达式格式错误");
        }
        if (StrUtil.isNotEmpty(resources.getSub_task())) {
            List<String> tasks = Arrays.asList(resources.getSub_task().split("[,，]"));
            if (tasks.contains(resources.getJob_id())) {
                throw new BadRequestException("子任务中不能添加当前任务ID");
            }
        }
        resources.setUpdate_id(SecurityUtils.getCurrentUserId());
        resources.setUpdate_name(SecurityUtils.getCurrentNickName());
        resources.setUpdate_time(DateUtil.now());
        quartzJobMapper.updateById(resources);
        quartzManage.updateJobCron(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateIsPause(SysQuartzJob quartzJob) {
        if (quartzJob.getIs_pause()) {
            quartzManage.resumeJob(quartzJob);
            quartzJob.setIs_pause(false);
        } else {
            quartzManage.pauseJob(quartzJob);
            quartzJob.setIs_pause(true);
        }
        quartzJobMapper.updateById(quartzJob);
    }

    @Override
    public void execution(SysQuartzJob quartzJob) {
        quartzManage.runJobNow(quartzJob);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteJob(Set<String> ids) {
        ids.forEach(id -> quartzManage.deleteJob(quartzJobMapper.selectById(id)));
        quartzJobMapper.deleteBatchIds(ids);
    }

    @Async
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executionSubJob(String[] tasks) throws InterruptedException {
        for (String id : tasks) {
            SysQuartzJob quartzJob = getById(id);
            // 执行任务
            String uuid = IdUtil.simpleUUID();
            quartzJob.setUuid(uuid);
            // 执行任务
            execution(quartzJob);
            // 获取执行状态，如果执行失败则停止后面的子任务执行
            Boolean result = (Boolean) redisUtils.get(uuid);
            while (result == null) {
                // 休眠5秒，再次获取子任务执行情况
                Thread.sleep(5000);
                result = (Boolean) redisUtils.get(uuid);
            }
            if (!result) {
                redisUtils.del(uuid);
                break;
            }
        }
    }

    @Override
    public List<SysQuartzJob> findByIsPauseIsFalse() {
        return quartzJobMapper.selectList(new LambdaQueryWrapper<SysQuartzJob>().eq(SysQuartzJob::getIs_pause, false));
    }
}
