package com.salpa.web.job;

import com.salpa.subject.domain.MonitorProjectSyncRule;
import com.salpa.subject.mapper.ProjectSyncRuleMapper;
import com.salpa.subject.service.DocumentService;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.util.Pair;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 用于启用 / 停用项目同步任务的调度器
 */
@Component
public class ProjectSyncJobScheduler {

    private static final String JOB_IDENTITY_PATTERN = "JOB_PROJECT[%s]";

    private static final String TRIGGER_IDENTITY_PATTERN = "TRIGGER_PROJECT[%s]";

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private ProjectSyncRuleMapper projectSyncRuleMapper;

    @Autowired
    private DocumentService documentService;

    /**
     * 启动 isAutoSync=true 的任务
     */
    @Scheduled(fixedRate = 1000)
    public void scheduleProjectSync() {
        List<JobDetail> list = getProjectJobDetails();
        for (JobDetail jobDetail : list) {
            System.out.println(jobDetail.getJobDataMap().getInt("projectId"));
            System.out.println(jobDetail.getJobDataMap().get("projectId"));
        }
        List<Integer> currentlyTriggerProjectIds = getProjectJobDetails()
                .stream()
                .map(job -> job.getJobDataMap().getInt("projectId"))
                .collect(Collectors.toList());
        // 查询已开启自动同步但还未运行的任务
        projectSyncRuleMapper.selectByIsAutoSyncAndNotInProjectIds(true, currentlyTriggerProjectIds)
                .stream()
                .map(this::jobAndTriggers)
                .forEach(jobAndTriggers -> {
                    try {
                        if (!scheduler.checkExists(jobAndTriggers.getSecond().getKey())) {
                            scheduler.scheduleJob(jobAndTriggers.getFirst(), jobAndTriggers.getSecond());
                        }
                    } catch (SchedulerException e) {
                        e.printStackTrace();
                    }
                });
    }

    /**
     * 停止 Rule.cron 更新过的任务
     */
    @Scheduled(fixedRate = 1000)
    public void reScheduleProjectSync() {
        List<JobDetail> list = getProjectJobDetails();
        for (JobDetail jobDetail : list) {
            System.out.println(jobDetail.getJobDataMap().getInt("projectId"));
            System.out.println(jobDetail.getJobDataMap().get("projectId"));
        }
        List<TriggerKey> triggerKeys = getProjectJobDetails()
                .stream()
                .filter(jobDetail -> {
                    JobDataMap dataMap = jobDetail.getJobDataMap();
                    System.out.println(dataMap.getInt("projectId"));
                    int projectId = dataMap.getInt("projectId");
                    String cron = dataMap.getString("cron");
                    return !projectSyncRuleMapper.existsByProjectIdAndCron(projectId, cron);
                })
                .map(jobDetail -> jobDetail.getJobDataMap().getInt("projectId"))
                .map(this::triggerKey)
                .collect(Collectors.toList());
        try {
            if (!triggerKeys.isEmpty()) {
                scheduler.unscheduleJobs(triggerKeys);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    /**
     * 停止 isAutoSync=false 的项目
     */
    @Scheduled(fixedRate = 1000)
    public void unScheduleProjectSync() {
        List<JobDetail> list = getProjectJobDetails();
        for (JobDetail jobDetail : list) {
            System.out.println(jobDetail.getJobDataMap().getInt("projectId"));
            System.out.println(jobDetail.getJobDataMap().get("projectId"));
        }
        List<Integer> projectIds = getProjectJobDetails()
                .stream()
                .map(job -> job.getJobDataMap().getInt("projectId"))
                .collect(Collectors.toList());
        // 查询已关闭自动同步但还在运行的任务
        List<TriggerKey> triggerKeys = projectSyncRuleMapper.selectByIsAutoSyncAndProjectIds(false, projectIds)
                .stream()
                .map(p -> triggerKey(p.getProjectId()))
                .collect(Collectors.toList());
        try {
            if (!triggerKeys.isEmpty()) {
                scheduler.unscheduleJobs(triggerKeys);
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    private List<JobDetail> getProjectJobDetails() {
        try {
            return scheduler.getJobKeys(GroupMatcher.anyGroup())
                    .stream()
                    .map(jobKey -> {
                        try {
                            JobDetail job = scheduler.getJobDetail(jobKey);
                            if (job.getJobDataMap().containsKey("projectId")) {
                                return Optional.of(job);
                            } else {
                                return Optional.<JobDetail>empty();
                            }
                        } catch (SchedulerException e) {
                            return Optional.<JobDetail>empty();
                        }
                    })
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .collect(Collectors.toList());
        } catch (SchedulerException e) {
            return Collections.emptyList();
        }
    }

    private Pair<JobDetail, Trigger> jobAndTriggers(MonitorProjectSyncRule rule) {
        JobDataMap dataMap = new JobDataMap();
        Integer projectId = rule.getProjectId();
        Integer ruleId = rule.getId();
        dataMap.put("projectId", projectId);
        dataMap.put("ruleId", ruleId);
        dataMap.put("cron", rule.getAutoSyncCron());
        dataMap.put("documentService", documentService);
        JobDetail job = JobBuilder.newJob()
                .ofType(com.salpa.web.job.ProjectSyncJob.class)
                .withIdentity(jobKey(projectId))
                .withDescription("auto sync project document")
                .usingJobData(dataMap)
                .build();
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(rule.getAutoSyncCron());
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerKey(projectId))
                .forJob(job)
                .withSchedule(scheduleBuilder)
                .startNow()
                .build();
        return Pair.of(job, trigger);
    }

    private TriggerKey triggerKey(Integer projectId) {
        return TriggerKey.triggerKey(String.format(TRIGGER_IDENTITY_PATTERN, projectId));
    }

    private JobKey jobKey(Integer projectId) {
        return JobKey.jobKey(String.format(JOB_IDENTITY_PATTERN, projectId));
    }
}
