package com.multictrl.modules.job.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.multictrl.common.constant.Constant;
import com.multictrl.common.page.PageData;
import com.multictrl.common.service.impl.BaseServiceImpl;
import com.multictrl.common.utils.ConvertUtils;
import com.multictrl.modules.job.dao.ScheduleJobDao;
import com.multictrl.modules.job.dto.ScheduleJobDTO;
import com.multictrl.modules.job.entity.ScheduleJobEntity;
import com.multictrl.modules.job.service.ScheduleJobService;
import com.multictrl.modules.job.utils.CronGenerator;
import com.multictrl.modules.job.utils.CronParser;
import com.multictrl.modules.job.utils.CronToChinese;
import com.multictrl.modules.job.utils.ScheduleUtils;
import com.multictrl.modules.security.user.SecurityUser;
import lombok.RequiredArgsConstructor;
import org.quartz.Scheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ScheduleJobServiceImpl extends BaseServiceImpl<ScheduleJobDao, ScheduleJobEntity> implements ScheduleJobService {
    private final Scheduler scheduler;

    @Override
    public PageData<ScheduleJobDTO> page(Map<String, Object> params) {
        IPage<ScheduleJobEntity> page = baseDao.pageList(
                getPage(params, Constant.CREATE_DATE, false),
                getWrapper(params)
        );
        page.getRecords().forEach(item -> {
            String days = item.getDays();
            if (StrUtil.isNotEmpty(days)) {
                List<Integer> dayList = Arrays.stream(days.trim().split(","))
                        .map(String::trim)
                        .filter(s -> !s.isEmpty())
                        .map(Integer::parseInt)
                        .collect(Collectors.toList());
                item.setDay(dayList);
            }
            if (item.getStatus() != -1) {
                String cronExpression = item.getCronExpression();
                String nextTime = CronParser.getNextTime(cronExpression);
                item.setNextRunTime(nextTime);
            }
            item.setCronRules(CronToChinese.convert(item.getYear(), item.getMonth(), item.getDays(), item.getHour(), item.getMinute(), item.getCronType()));
        });
        return getPageData(page, ScheduleJobDTO.class);
    }

    @Override
    public ScheduleJobDTO get(Long id) {
        ScheduleJobEntity entity = baseDao.getById(id);
        if (entity != null) {
            String days = entity.getDays();
            List<Integer> dayList = Arrays.stream(days.trim().split(","))
                    .map(String::trim)
                    .filter(s -> !s.isEmpty())
                    .map(Integer::parseInt)
                    .collect(Collectors.toList());
            entity.setDay(dayList);
        }

        return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);
    }

    private QueryWrapper<ScheduleJobEntity> getWrapper(Map<String, Object> params) {
        String beanName = (String) params.get("beanName");
        String jobName = (String) params.get("jobName");

        QueryWrapper<ScheduleJobEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(beanName), "s.bean_name", beanName);
        wrapper.like(StrUtil.isNotBlank(jobName), "s.job_name", jobName);

        return wrapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(ScheduleJobDTO dto) {
        ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
        entity.setDeptId(SecurityUser.getDeptId());
        String cron = CronGenerator.getCron(dto.getCronType(), dto.getYear(), dto.getMonth(), dto.getDay(), dto.getHour(), dto.getMinute());
        entity.setCronExpression(cron);
        entity.setDays(StrUtil.join(",", dto.getDay()));

        entity.setStatus(Constant.ScheduleStatus.PAUSE.getValue());
        this.insert(entity);

        ScheduleUtils.createScheduleJob(scheduler, entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(ScheduleJobDTO dto) {
        ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
        entity.setDeptId(SecurityUser.getDeptId());
        String cron = CronGenerator.getCron(dto.getCronType(), dto.getYear(), dto.getMonth(), dto.getDay(), dto.getHour(), dto.getMinute());
        entity.setCronExpression(cron);
        entity.setDays(StrUtil.join(",", dto.getDay()));
        if (dto.getStatus() == Constant.ScheduleStatus.FAILURE.getValue()) {
            entity.setStatus(Constant.ScheduleStatus.PAUSE.getValue());
            ScheduleUtils.createScheduleJob(scheduler, entity);
        } else {
            ScheduleUtils.updateScheduleJob(scheduler, entity);
        }

        this.updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatch(Long[] ids) {
        for (Long id : ids) {
            ScheduleUtils.deleteScheduleJob(scheduler, id);
        }

        //删除数据
        this.deleteBatchIds(Arrays.asList(ids));
    }

    @Override
    public int updateBatch(Long[] ids, int status) {
        Map<String, Object> map = new HashMap<>(2);
        map.put("ids", ids);
        map.put("status", status);
        return baseDao.updateBatch(map);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void run(Long[] ids) {
        List<Long> failure = new ArrayList<>();
        for (Long id : ids) {
            //是否判断过期
            ScheduleJobDTO jobDTO = get(id);
            int result = ScheduleUtils.run(scheduler, this.selectById(id), jobDTO.getCronType() == 1);
            if (result == Constant.ScheduleStatus.FAILURE.getValue()) {
                failure.add(id);
            }
        }
        if (CollectionUtil.isNotEmpty(failure)) {
            updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pause(Long[] ids) {
        List<Long> failure = new ArrayList<>();
        List<Long> normal = new ArrayList<>();
        for (Long id : ids) {
            //是否判断过期
            ScheduleJobDTO jobDTO = get(id);
            int result = ScheduleUtils.pauseJob(scheduler, id, jobDTO.getCronType() == 1);
            if (result == Constant.ScheduleStatus.FAILURE.getValue()) {
                failure.add(id);
            } else {
                normal.add(id);
            }
        }
        if (CollectionUtil.isNotEmpty(failure)) {
            updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue());
        }
        if (CollectionUtil.isNotEmpty(normal)) {
            updateBatch(normal.toArray(new Long[0]), Constant.ScheduleStatus.PAUSE.getValue());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resume(Long[] ids) {
        List<Long> failure = new ArrayList<>();
        List<Long> normal = new ArrayList<>();
        for (Long id : ids) {
            //是否判断过期
            ScheduleJobDTO jobDTO = get(id);
            int result = ScheduleUtils.resumeJob(scheduler, id, jobDTO.getCronType() == 1);
            if (result == Constant.ScheduleStatus.FAILURE.getValue()) {
                failure.add(id);
            } else {
                normal.add(id);
            }
        }
        if (CollectionUtil.isNotEmpty(failure)) {
            updateBatch(failure.toArray(new Long[0]), Constant.ScheduleStatus.FAILURE.getValue());
        }
        if (CollectionUtil.isNotEmpty(normal)) {
            updateBatch(normal.toArray(new Long[0]), Constant.ScheduleStatus.NORMAL.getValue());
        }
    }
}