package com.ruoyi.cron.api;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.lock.annotation.Lock4j;
import com.ruoyi.common.annotation.Dev;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.Page;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.validate.AddGroup;
import com.ruoyi.common.core.validate.EditGroup;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.IdUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.cron.document.CronTask;
import com.ruoyi.cron.document.CronTaskLog;
import com.ruoyi.cron.event.CronTaskChangeEvent;
import com.ruoyi.cron.event.CronTaskEvent;
import com.ruoyi.cron.query.CronTaskLogQuery;
import com.ruoyi.cron.runner.CronBeanPostProcessor;
import com.ruoyi.cron.runner.CronRunner;
import com.ruoyi.cron.task.CronTaskTestTask;
import com.ruoyi.cron.vo.CronTaskVo;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.constraints.NotEmpty;

import java.util.List;
import java.util.TreeSet;
import java.util.stream.Collectors;

import static com.ruoyi.common.utils.MongoUtil.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/system/cron/")
public class CronTaskApi {

  private final MongoTemplate template;

  private final CronBeanPostProcessor processor;

  private final CronRunner runner;

  /**
   * 执行测试任务一
   */
  @GetMapping("/test1")
  @Dev
  @SaIgnore
  public void test1() {
    SpringUtils.publishEvent(new CronTaskEvent(null, CronTaskVo.id(ClassUtil.getPublicMethod(CronTaskTestTask.class, "test")), null));
  }

  /**
   * 执行测试任务二
   */
  @GetMapping("/test2")
  @Dev
  @SaIgnore
  public void test2() {
    SpringUtils.publishEvent(new CronTaskEvent(null, CronTaskVo.id(ClassUtil.getPublicMethod(CronTaskTestTask.class, "test2", java.util.Date.class, String.class, String.class, int.class)), ListUtil.of("new java.util.Date()", "#bean.name1()", "#root.getBean('cronTaskTestTask').name2()", "T(Integer).MAX_VALUE")));
  }

  /**
   * 执行测试任务三
   */
  @GetMapping("/test3")
  @Dev
  @SaIgnore
  public void test3() {
    SpringUtils.publishEvent(new CronTaskEvent(null, CronTaskVo.id(ClassUtil.getPublicMethod(CronTaskTestTask.class, "test3")), ListUtil.of("new java.util.Date()", "#bean.name1()", "#root.getBean('cronTaskTestTask').name2()", "T(Integer).MAX_VALUE")));
  }

  @GetMapping("/list-{group}")
  @SaCheckPermission("sys:cron:query")
  public List<CronTaskVo> list(@PathVariable int group) {
    return processor.getList().stream().filter(cronTask -> cronTask.getGroup() == group).collect(Collectors.toList());
  }

  @GetMapping("/current-{group}")
  @SaCheckPermission("sys:cron:query")
  public Object current(@PathVariable int group) {
    return runner.getCurrentLogs().stream().filter(l -> l.getGroupId() == group).collect(Collectors.toList());
  }

  @PostMapping
  @SaCheckPermission("sys:cron:add")
  @Log(title = "定时任务", businessType = BusinessType.INSERT)
  @Lock4j
  public void add(@Validated(AddGroup.class) @RequestBody CronTask cronTask) {
    cronTask.setId(Long.valueOf(IdUtils.nextDateId(CronTask.class.getSimpleName(), 15)));
    cronTask.setCreateTime(new java.util.Date());
    cronTask.setEnabled(true);
    cronTask.setUserId(LoginHelper.getUserId());
    template.insert(cronTask);
    SpringUtils.publishEvent(new CronTaskChangeEvent(cronTask, CronTaskChangeEvent.ChangeType.ADD));
  }

  @PostMapping("/start")
  @SaCheckPermission("sys:cron:query")
  @Lock4j
  public void start(Long cronId, String taskId, String[] paramEls) {
    SpringUtils.publishEvent(new CronTaskEvent(cronId, taskId, ListUtil.of(paramEls)));
  }

  @PutMapping
  @SaCheckPermission("sys:cron:update")
  @Log(title = "定时任务", businessType = BusinessType.UPDATE)
  @Lock4j
  public void update(@Validated(EditGroup.class) @RequestBody CronTask cronTask) {
    CronTask old = template.findById(cronTask.getId(), CronTask.class);
    if (ObjUtil.isNull(old)) {
      throw new RuntimeException("没有该定时任务");
    }
    BeanUtil.copyProperties(cronTask, old, CopyOptions.create(CronTask.class, true, "createTime", "userId"));

    template.save(old);
    SpringUtils.publishEvent(new CronTaskChangeEvent(old, CronTaskChangeEvent.ChangeType.UPDATE));
  }

  @Log(title = "定时任务", businessType = BusinessType.DELETE)
  @DeleteMapping("/{ids}")
  @SaCheckPermission("sys:cron:remove")
  @Lock4j
  public void remove(@NotEmpty(message = "编号不能为空") @PathVariable Long[] ids) {
    for (Long id : ids) {
      CronTask old = template.findById(id, CronTask.class);
      if (ObjUtil.isNotNull(old)) {
        template.remove(old);
        SpringUtils.publishEvent(new CronTaskChangeEvent(old, CronTaskChangeEvent.ChangeType.DELETE));
      }
    }
  }

  @GetMapping("/page")
  @SaCheckPermission("sys:cron:query")
  public Page<CronTask> page(Page<CronTask> page, CronTask dataCron, Boolean pid$exists) {
    return findPage(CronTask.class, page, eq(dataCron), criteria(conditions().put("pid$exists", pid$exists)));
  }

  @GetMapping("/log-page")
  @SaCheckPermission("sys:cron:query")
  public Page<CronTaskLog> logPage(Page<CronTaskLog> page, CronTaskLog cronTaskLog, CronTaskLogQuery query) {
    return findPage(CronTaskLog.class, page, eq(cronTaskLog), query.where());
  }
}
