package com.lanyuanxiaoyao.leopard.server.controller;

import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.lanyuanxiaoyao.leopard.core.entity.Task;
import com.lanyuanxiaoyao.leopard.core.service.TaskService;
import com.lanyuanxiaoyao.service.template.controller.GlobalResponse;
import com.lanyuanxiaoyao.service.template.controller.SimpleControllerSupport;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.Map;
import java.util.function.Function;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 任务列表
 *
 * @author lanyuanxiaoyao
 * @version 20250829
 */
@Slf4j
@RestController
@RequestMapping("task")
public class TaskController extends SimpleControllerSupport<Task, Void, TaskController.ListItem, TaskController.DetailItem> {
  private final TaskService taskService;

  public TaskController(TaskService service) {
    super(service);
    this.taskService = service;
  }

  @Override
  public GlobalResponse<Long> save(Void unused) {
    throw new UnsupportedOperationException();
  }

  @PostMapping("execute")
  public GlobalResponse<Object> execute(@RequestBody ExecuteRequest request) {
    taskService.execute(request.templateId(), request.params());
    return GlobalResponse.responseSuccess();
  }

  @GetMapping("template/list")
  public GlobalResponse<Map<String, Object>> templateList() {
    var templates = taskService.getTemplates()
      .stream()
      .sorted(Comparator.comparing(TaskService.TaskTemplate::name))
      .toList();
    return GlobalResponse.responseCrudData(templates, templates.size());
  }

  @Override
  protected Function<Void, Task> saveItemMapper() {
    throw new UnsupportedOperationException();
  }

  private TaskCost calculateCost(LocalDateTime start, LocalDateTime finish) {
    if (ObjectUtil.isNull(start)) {
      return new TaskCost(null, null);
    } else if (ObjectUtil.isNull(finish)) {
      finish = LocalDateTime.now();
    }
    var duration = Duration.between(start, finish).toMillis();
    return new TaskCost(
      duration,
      DateUtil.formatBetween(duration, BetweenFormatter.Level.SECOND)
    );
  }

  @Override
  protected Function<Task, ListItem> listItemMapper() {
    return task -> {
      var cost = calculateCost(task.getLaunchedTime(), task.getFinishedTime());
      return new ListItem(
        task.getId(),
        task.getName(),
        task.getDescription(),
        task.getStatus(),
        task.getLaunchedTime(),
        task.getFinishedTime(),
        cost.cost(),
        cost.costText(),
        task.getStep()
      );
    };
  }

  @Override
  protected Function<Task, DetailItem> detailItemMapper() {
    return task -> {
      var cost = calculateCost(task.getLaunchedTime(), task.getFinishedTime());
      return new DetailItem(
        task.getId(),
        task.getName(),
        task.getDescription(),
        task.getStatus(),
        task.getError(),
        task.getResult(),
        task.getLaunchedTime(),
        task.getFinishedTime(),
        cost.cost(),
        cost.costText(),
        task.getStep()
      );
    };
  }

  public record ListItem(
    Long id,
    String name,
    String description,
    Task.Status status,
    LocalDateTime launchedTime,
    LocalDateTime finishedTime,
    Long cost,
    String costText,
    Double step
  ) {
  }

  public record DetailItem(
    Long id,
    String name,
    String description,
    Task.Status status,
    String error,
    String result,
    LocalDateTime launchedTime,
    LocalDateTime finishedTime,
    Long cost,
    String costText,
    Double step
  ) {
  }

  public record ExecuteRequest(String templateId, Map<String, Object> params) {
  }

  public record TaskCost(Long cost, String costText) {
  }
}
