package com.css.simulation.resource.monitor.infra.scheduler;

import api.common.util.ObjectUtil;
import com.css.simulation.resource.monitor.acl.service.ProjectAclService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ScheduledFuture;

@Component
@Slf4j
public class ProjectScheduler implements SchedulingConfigurer {

    @Resource
    private ProjectAclService projectAclService;

    static ProjectAclService staticProjectAclService;

    private ScheduledTaskRegistrar taskRegistrar;
    private final Map<String, ScheduledFuture> scheduledFutures = new HashMap<>();
    private final Map<String, ProjectTask> projectTasks = new HashMap<>();

    @PostConstruct
    public void postConstruct() {
        staticProjectAclService = this.projectAclService;
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        this.taskRegistrar = taskRegistrar;
    }

    /**
     * 初始化任务
     */
    public int init(List<ProjectTask> projectTask) {
        if (ObjectUtil.isNull(projectTask)) {
            return 0;
        }
        //停止旧任务
        destroy();
        int count = 0;
        for (ProjectTask task : projectTask) {
            if (start(task)) {
                count++;
            }
        }
        ;
        //返回启动成功的任务数量
        return count;
    }

    /**
     * 开启任务
     */
    public boolean start(ProjectTask task) {
        if (!validate(task)) {
            return false;
        }
        String projectId = task.getProjectId();
        String cron = task.getCron();
        //任务已经存在，并且cron表达式没有变动，直接返回
        if (scheduledFutures.containsKey(projectId)) {
            if (projectTasks.containsKey(projectId)) {
                ProjectTask projectTask = projectTasks.get(projectId);
                if (projectTask.getCron().equals(cron)) {
                    return true;
                } else {
                    //有变动就停止
                    scheduledFutures.get(projectId).cancel(false);
                }
            } else {
                return false;
            }
        }
        //开启新任务
        ScheduledFuture<?> schedule = Objects.requireNonNull(taskRegistrar.getScheduler()).schedule(task, triggerContext -> {
            CronTrigger cronTrigger = new CronTrigger(cron);
            return cronTrigger.nextExecutionTime(triggerContext);
        });
        //放入缓存
        projectTasks.put(projectId, task);
        scheduledFutures.put(projectId, schedule);
        return true;
    }

    /**
     * 停止任务
     */
    public boolean stop(ProjectTask task) {
        if (ObjectUtil.isNull(task) || ObjectUtil.isNull(task.getProjectId())) {
            return false;
        }
        //任务已经存在，停止
        String projectId = task.getProjectId();
        if (scheduledFutures.containsKey(projectId)) {
            scheduledFutures.get(projectId).cancel(false);
            //清除缓存
            projectTasks.remove(projectId);
            scheduledFutures.remove(projectId);
            return true;
        }
        return false;
    }

    /**
     * 清空所有任务
     */
    public void destroy() {
        //停止现有任务
        scheduledFutures.forEach((k, v) -> {
            //运行中不允许中断，完成后再停止
            v.cancel(false);
        });
        //清空缓存
        scheduledFutures.clear();
        projectTasks.clear();
    }

    private boolean validate(ProjectTask task) {
        if (ObjectUtil.isNull(task)) {
            return false;
        }
        if (ObjectUtil.isNull(task.getProjectId()) || ObjectUtil.isNull(task.getCron())) {
            return false;
        }
        //校验cron表达式
        try {
            CronExpression.parse(task.getCron());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 继承该类，重写run方法，即可自定义任务执行逻辑
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ProjectTask implements Runnable {
        private String projectId;
        private String cron;

        @Override
        public void run() {
            log.info("projectTask [" + projectId + "] run : " + new Date());
            Map<String, String> projectParam = new HashMap<>();
            projectParam.put("id", projectId);
            ProjectScheduler.staticProjectAclService.runProject(projectParam);
        }
    }

}
