package com.usky.rule.config;

import com.usky.rule.util.RuleEngineUtil;
import com.usky.rule.vo.action.RuleEngineAction;
import com.usky.rule.vo.constraint.CronConstraint;
import com.usky.rule.vo.constraint.DeviceConstraint;
import com.usky.rule.jobs.ConsumptionJob;
import com.usky.rule.listeners.CommonListener;
import com.usky.rule.subscribe.TriggerDeviceUtil;

import java.util.ArrayList;
import java.util.List;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Component;

@Component
public class CronTaskManager {
    private Scheduler scheduler;
    private TriggerDeviceUtil triggerDeviceUtil;

    public void addJob(String jobName, String jobGroup, String cronExpression, Class<? extends Job> jobClass, List<CronConstraint> cronConstraintList, List<DeviceConstraint> deviceConstraints, List<RuleEngineAction> actions, Long ruleEngineId, Long projectId, Long spaceId) {
        JobDataMap jobDataMap = new JobDataMap();
        jobDataMap.put("actions", actions);
        jobDataMap.put("ruleEngineId", ruleEngineId);
        jobDataMap.put("projectId", projectId);
        jobDataMap.put("spaceId", spaceId);
        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).usingJobData(jobDataMap).build();
        CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).build();

        try {
            if (cronConstraintList != null || deviceConstraints != null) {
                CommonListener jobListener = new CommonListener(this.getListenerName(ruleEngineId), this.triggerDeviceUtil, cronConstraintList, deviceConstraints);
                this.scheduler.getListenerManager().addJobListener(jobListener, GroupMatcher.jobGroupEquals(jobGroup));
            }

            this.scheduler.scheduleJob(jobDetail, cronTrigger);
        } catch (SchedulerException e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String getListenerName(Long ruleEngineId) {
        return "ruleEngineListener-" + ruleEngineId;
    }

    public void updateJob(String jobName, String jobGroup, String cronExpression) throws SchedulerException {
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        CronTrigger cronTrigger = (CronTrigger)this.scheduler.getTrigger(triggerKey);
        if (cronTrigger == null) {
            throw new SchedulerException("定时任务不存在");
        } else {
            String oldCronExpression = cronTrigger.getCronExpression();
            if (!oldCronExpression.equalsIgnoreCase(cronExpression)) {
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
                cronTrigger = (CronTrigger)cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
                this.scheduler.rescheduleJob(triggerKey, cronTrigger);
            }

        }
    }

    public void deleteJob(String jobName, String jobGroup) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        if (this.scheduler.checkExists(jobKey)) {
            this.scheduler.deleteJob(jobKey);
        }

    }

    public boolean deleteAllJobsInJobGroup(Long ruleEngineId) {
        String jobGroup = RuleEngineUtil.getJobGroup(ruleEngineId);

        try {
            List<JobKey> jobKeys = new ArrayList(this.scheduler.getJobKeys(GroupMatcher.jobGroupEquals(jobGroup)));
            if (!jobKeys.isEmpty()) {
                this.scheduler.deleteJobs(jobKeys);
                this.scheduler.getListenerManager().removeJobListenerMatcher(this.getListenerName(ruleEngineId), GroupMatcher.jobGroupEquals(jobGroup));
            }

            return true;
        } catch (SchedulerException var4) {
            return false;
        }
    }

    public void pauseAllJobs() throws SchedulerException {
        this.scheduler.pauseAll();
    }

    public void resumeAllJobs() throws SchedulerException {
        this.scheduler.resumeAll();
    }

    public void resumePausedJobs() throws SchedulerException {
        for(String groupName : this.scheduler.getPausedTriggerGroups()) {
            for(JobKey jobKey : this.scheduler.getJobKeys(GroupMatcher.groupEquals(groupName))) {
                this.scheduler.resumeJob(jobKey);
            }
        }

    }

    public void performConsumptionTask() {
        JobKey consumptionKey = JobKey.jobKey("consumption", "device");
        try {
            if (this.scheduler.checkExists(consumptionKey)) {
                return;
            }
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }

        JobDetail consumptionJob = JobBuilder.newJob(ConsumptionJob.class).withIdentity(consumptionKey).build();
        CronTrigger consumptionTrigger = (CronTrigger)TriggerBuilder.newTrigger().forJob(consumptionJob).withIdentity("consumptionTrigger").withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")).build();
//        JobDetail spaceJob = JobBuilder.newJob(SpaceJob.class).withIdentity("space").build();
//        CronTrigger spaceTrigger = (CronTrigger)TriggerBuilder.newTrigger().forJob(spaceJob).withIdentity("spaceTrigger", "space").withSchedule(CronScheduleBuilder.cronSchedule("0 0 * * * ?")).build();

        try {
            this.scheduler.scheduleJob(consumptionJob, consumptionTrigger);
//            this.scheduler.scheduleJob(spaceJob, spaceTrigger);
        } catch (SchedulerException e) {
            throw new RuntimeException(e);
        }
    }

    public CronTaskManager(final Scheduler scheduler, final TriggerDeviceUtil triggerDeviceUtil) {
        this.scheduler = scheduler;
        this.triggerDeviceUtil = triggerDeviceUtil;
    }
}
