package com.usky.rule.listeners;

import com.usky.rule.util.CronUtil;
import com.usky.rule.util.DateTimeUtil;
import com.usky.rule.enums.TriggerTypeEnum;
import com.usky.rule.vo.constraint.CronConstraint;
import com.usky.rule.vo.constraint.DeviceConstraint;
import com.usky.rule.vo.log.BaseLog;
import com.usky.rule.vo.log.CronTriggerLog;
import com.usky.rule.vo.log.RuleEngineDetailLog;
import com.usky.rule.subscribe.TriggerDeviceUtil;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.quartz.CronTrigger;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CommonListener implements JobListener {
    private static final Logger LOGGER = LoggerFactory.getLogger(CommonListener.class);
    private final String name;
    private final TriggerDeviceUtil triggerDeviceUtil;
    private final List<CronConstraint> cronConstraintList;
    private final List<DeviceConstraint> deviceConstraintList;

    public CommonListener(String name, TriggerDeviceUtil triggerDeviceUtil, List<CronConstraint> cronConstraintList, List<DeviceConstraint> deviceConstraints) {
        this.name = name;
        this.triggerDeviceUtil = triggerDeviceUtil;
        this.cronConstraintList = cronConstraintList;
        this.deviceConstraintList = deviceConstraints;
    }

    public String getName() {
        return this.name;
    }

    public void jobToBeExecuted(JobExecutionContext context) {
        LocalDateTime now = LocalDateTime.now();
        List<BaseLog> constraintLogs = new ArrayList();
        RuleEngineDetailLog ruleEngineDetailLog = initRuleEngineDetailLog((CronTrigger)context.getTrigger(), now);
        ruleEngineDetailLog.setConstraints(constraintLogs);
        LOGGER.info("Job {} is about to be executed", context.getJobDetail().getKey());
        if (this.cronConstraintList != null) {
            Date evalAt = DateTimeUtil.localDateTimeToDate(now);
            for (CronConstraint cronConstraint : this.cronConstraintList) {
                if (!CronUtil.isCronSatisfiedBy(cronConstraint.getCron(), evalAt)) {
                    throw new RuntimeException(" cron condition not met, aborting job execution.");
                }

                CronTriggerLog cronConstraintLog = new CronTriggerLog();
                cronConstraintLog.setCronExp(cronConstraint.getCron());
                cronConstraintLog.setTime(DateTimeUtil.format(now));
                BaseLog cronBaseLog = new BaseLog();
                cronBaseLog.setDetail(cronConstraintLog);
                cronBaseLog.setType(TriggerTypeEnum.CRON.getType());
                constraintLogs.add(cronBaseLog);
            }
        }

        if (this.deviceConstraintList != null && !this.triggerDeviceUtil.meetConstraintAction(this.deviceConstraintList, ruleEngineDetailLog)) {
            throw new RuntimeException("device constraint condition not met, aborting job execution.");
        } else {
            context.put("detail", ruleEngineDetailLog);
        }
    }

    public static RuleEngineDetailLog initRuleEngineDetailLog(CronTrigger trigger, LocalDateTime now) {
        RuleEngineDetailLog ruleEngineDetailLog = new RuleEngineDetailLog();
        List<BaseLog> triggerLogs = new ArrayList();
        ruleEngineDetailLog.setTriggers(triggerLogs);
        String cronExpression = trigger.getCronExpression();
        CronTriggerLog cronTriggerLog = new CronTriggerLog();
        cronTriggerLog.setCronExp(cronExpression);
        cronTriggerLog.setTime(DateTimeUtil.format(now));
        BaseLog baseLog = new BaseLog();
        baseLog.setDetail(cronTriggerLog);
        baseLog.setType(TriggerTypeEnum.CRON.getType());
        triggerLogs.add(baseLog);
        return ruleEngineDetailLog;
    }

    public void jobExecutionVetoed(JobExecutionContext context) {
        LOGGER.info("Job {} was vetoed from being executed", context.getJobDetail().getKey());
    }

    public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
        LOGGER.info("Job {} was executed", context.getJobDetail().getKey());
    }
}
