package com.kamco.cd.training.model;

import com.kamco.cd.training.common.dto.MonitorDto;
import com.kamco.cd.training.common.enums.ModelType;
import com.kamco.cd.training.common.service.SystemMonitorService;
import com.kamco.cd.training.config.api.ApiResponseDto;
import com.kamco.cd.training.dataset.dto.DatasetDto;
import com.kamco.cd.training.dataset.dto.DatasetDto.DatasetReq;
import com.kamco.cd.training.dataset.dto.DatasetDto.SelectDataSet;
import com.kamco.cd.training.model.dto.ModelConfigDto;
import com.kamco.cd.training.model.dto.ModelTrainMngDto;
import com.kamco.cd.training.model.dto.ModelTrainMngDto.ListDto;
import com.kamco.cd.training.model.service.ModelTrainMngService;
import com.kamco.cd.training.train.service.ModelTestMetricsJobService;
import com.kamco.cd.training.train.service.ModelTrainMetricsJobService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@Tag(name = "모델학습 관리", description = "어드민 홈 > 모델학습관리 > 모델관리 > 목록")
@RequestMapping("/api/models")
public class ModelTrainMngApiController {
  private final ModelTrainMngService modelTrainMngService;
  private final ModelTrainMetricsJobService modelTrainMetricsJobService;
  private final ModelTestMetricsJobService modelTestMetricsJobService;
  private final SystemMonitorService systemMonitorService;

  @Operation(summary = "모델학습 목록 조회", description = "모델학습 목록 조회 API")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Page.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/list")
  public ApiResponseDto<Page<ListDto>> findByModelList(
      @Parameter(
              description = "상태코드",
              example = "IN_PROGRESS",
              schema = @Schema(allowableValues = {"", "IN_PROGRESS", "COMPLETED"}))
          @RequestParam(required = false)
          String status,
      @Parameter(
              description = "모델",
              example = "G1",
              schema = @Schema(allowableValues = {"G1", "G2", "G3", "G4"}))
          @RequestParam(required = false)
          String modelNo,
      @Parameter(description = "페이지 번호") @RequestParam(defaultValue = "0") int page,
      @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "20") int size) {
    ModelTrainMngDto.SearchReq searchReq =
        new ModelTrainMngDto.SearchReq(status, modelNo, page, size);
    return ApiResponseDto.ok(modelTrainMngService.getModelList(searchReq));
  }

  @Operation(summary = "학습 모델 삭제", description = "학습 모델 삭제 API")
  @ApiResponses(
      value = {
        @ApiResponse(responseCode = "200", description = "삭제 성공", content = @Content),
        @ApiResponse(responseCode = "409", description = "G1_000001 삭제 불가", content = @Content)
      })
  @DeleteMapping("/{uuid}")
  public ApiResponseDto<Void> deleteModelTrain(
      @Parameter(description = "학습 모델 uuid", example = "f2b02229-90f2-45f5-92ea-c56cf1c29f79")
          @PathVariable
          UUID uuid) {
    modelTrainMngService.deleteModelTrain(uuid);
    return ApiResponseDto.ok(null);
  }

  @Operation(summary = "학습 모델 등록", description = "학습 모델 등록 API")
  @ApiResponses(
      value = {
        @ApiResponse(responseCode = "200", description = "등록 성공", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @PostMapping
  public ApiResponseDto<UUID> createModelTrain(@Valid @RequestBody ModelTrainMngDto.AddReq req) {
    return ApiResponseDto.ok(modelTrainMngService.createModelTrain(req));
  }

  @Operation(summary = "모델학습 config 정보 조회", description = "모델학습 config 정보 조회 API")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = ModelConfigDto.Basic.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/config/{uuid}")
  public ApiResponseDto<ModelConfigDto.Basic> updateModelTrain(
      @Parameter(description = "모델학습 uuid", example = "7fbdff54-ea87-4b02-90d1-955fa2a3457e")
          @PathVariable
          UUID uuid) {
    return ApiResponseDto.ok(modelTrainMngService.getModelConfigByModelId(uuid));
  }

  @Operation(summary = "모델별 데이터셋 목록 조회", description = "모델별 데이터셋 목록 조회 API")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "조회 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = DatasetDto.Basic.class))),
        @ApiResponse(responseCode = "404", description = "데이터셋을 찾을 수 없음", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/select-dataset-list")
  public ApiResponseDto<List<SelectDataSet>> getDatasetSelectList(
      @Parameter(
              description = "모델 구분",
              example = "",
              schema = @Schema(allowableValues = {"G1", "G2", "G3", "G4"}))
          @RequestParam
          ModelType modelType,
      @Parameter(
              description = "선택 구분",
              example = "",
              schema = @Schema(allowableValues = {"CURRENT", "DELIVER", "PRODUCTION"}))
          @RequestParam
          String selectType) {
    DatasetReq req = new DatasetReq();
    req.setModelNo(modelType.getId());
    req.setDataType(selectType);
    return ApiResponseDto.ok(modelTrainMngService.getDatasetSelectList(req));
  }

  @Operation(
      summary = "모델학습 1단계/2단계 실행중인 것이 있는지 count",
      description = "모델학습 1단계/2단계 실행중인 것이 있는지 count")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/ing-training-cnt")
  public ApiResponseDto<Long> findModelStep1InProgressCnt() {
    return ApiResponseDto.ok(modelTrainMngService.findModelStep1InProgressCnt());
  }

  @Operation(
      summary = "스케줄러 findTrainValidMetricCsvFiles",
      description = "스케줄러 findTrainValidMetricCsvFiles")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/schedule-trainvalid")
  public ApiResponseDto<Long> findTrainValidMetricCsvFiles() {
    modelTrainMetricsJobService.findTrainValidMetricCsvFiles();
    return ApiResponseDto.ok(null);
  }

  @Operation(summary = "스케줄러 findTestMetricCsvFiles", description = "스케줄러 findTestMetricCsvFiles")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/schedule-test")
  public ApiResponseDto<Long> findTestValidMetricCsvFiles() throws IOException {
    modelTestMetricsJobService.findTestValidMetricCsvFiles();
    return ApiResponseDto.ok(null);
  }

  @Operation(summary = "학습서버 시스템 사용율 조회", description = "cpu, gpu, memory 사용율 조회")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/monitor")
  public ApiResponseDto<MonitorDto> getSystem() throws IOException {
    return ApiResponseDto.ok(systemMonitorService.get());
  }

  @Operation(summary = "모델학습 1단계/2단계 실행중인 것 id 정보", description = "모델학습 1단계/2단계 실행중인 것 id 정보")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/ing-model")
  public ApiResponseDto<ModelTrainMngDto.InProgressModel> findInprogressModel() {
    return ApiResponseDto.ok(modelTrainMngService.findInprogressModel());
  }

  @Operation(summary = "모델학습 진행율 퍼센트", description = "모델학습 진행율 퍼센트")
  @ApiResponses(
      value = {
        @ApiResponse(
            responseCode = "200",
            description = "검색 성공",
            content =
                @Content(
                    mediaType = "application/json",
                    schema = @Schema(implementation = Long.class))),
        @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content),
        @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
      })
  @GetMapping("/progress-percent/{uuid}")
  public ApiResponseDto<ModelTrainMngDto.ProgressPercent> findTrainProgressPercent(
      @PathVariable UUID uuid) {
    return ApiResponseDto.ok(modelTrainMngService.findTrainProgressPercent(uuid));
  }
}
