package com.lanyuanxiaoyao.leopard.server.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.lanyuanxiaoyao.leopard.core.service.TaskService;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class QuartzService {
  private final Scheduler scheduler;

  public QuartzService(Scheduler scheduler) {
    this.scheduler = scheduler;
  }

  public List<QuartzTask> list() throws SchedulerException {
    var tasks = new ArrayList<QuartzTask>();
    for (var key : scheduler.getJobKeys(GroupMatcher.anyGroup())) {
      var detail = scheduler.getJobDetail(key);
      var trigger = (CronTrigger) scheduler.getTriggersOfJob(key).getFirst();
      tasks.add(new QuartzTask(
        detail.getKey().getName(),
        detail.getJobDataMap().getString("template_id"),
        trigger.getCronExpression(),
        scheduler.getTriggerState(trigger.getKey()),
        ObjectUtil.isNull(trigger.getPreviousFireTime()) ? null : LocalDateTime.ofInstant(trigger.getPreviousFireTime().toInstant(), ZoneId.systemDefault()),
        ObjectUtil.isNull(trigger.getNextFireTime()) ? null : LocalDateTime.ofInstant(trigger.getNextFireTime().toInstant(), ZoneId.systemDefault())
      ));
    }
    return tasks;
  }

  public void save(String templateId, String cron) throws SchedulerException {
    var detail = JobBuilder.newJob(TaskExecutionJob.class)
      .withIdentity("task_execution_" + IdUtil.fastUUID())
      .usingJobData("template_id", templateId)
      .storeDurably()
      .build();
    var trigger = TriggerBuilder.newTrigger()
      .forJob(detail)
      .withIdentity("task_execution_" + IdUtil.fastUUID())
      .withSchedule(
        CronScheduleBuilder.cronSchedule(cron)
          .withMisfireHandlingInstructionDoNothing()
      )
      .build();
    scheduler.scheduleJob(detail, trigger);
  }

  public void pause(String jobKey) throws SchedulerException {
    scheduler.pauseJob(JobKey.jobKey(jobKey));
  }

  public void resume(String jobKey) throws SchedulerException {
    scheduler.resumeJob(JobKey.jobKey(jobKey));
  }

  public void remove(String jobKey) throws SchedulerException {
    pause(jobKey);
    scheduler.deleteJob(JobKey.jobKey(jobKey));
  }

  @Slf4j
  public static class TaskExecutionJob extends QuartzJobBean {
    private final TaskService taskService;

    public TaskExecutionJob(TaskService taskService) {
      this.taskService = taskService;
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void executeInternal(JobExecutionContext context) {
      var dataMap = context.getMergedJobDataMap();
      var templateId = dataMap.getString("template_id");
      if (StrUtil.isNotBlank(templateId)) {
        var params = (Map<String, Object>) dataMap.getOrDefault("params", Map.of());
        taskService.execute(templateId, params, true);
      }
    }
  }

  public record QuartzTask(
    String key,
    String templateId,
    String cron,
    Trigger.TriggerState status,
    LocalDateTime previousFireTime,
    LocalDateTime nextFireTime
  ) {
  }
}
