package com.kamco.cd.training.model.dto;

import com.kamco.cd.training.common.dto.HyperParam;
import com.kamco.cd.training.common.enums.TrainStatusType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.utils.HeaderUtil;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

public class ModelTrainMngDto {
  @Schema(name = "모델학습관리 목록", description = "모델학습관리 목록")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class Basic {

    private Long id;
    private UUID uuid;
    private String modelVer;
    @JsonFormatDttm private ZonedDateTime startDttm;
    @JsonFormatDttm private ZonedDateTime step1StrtDttm;
    @JsonFormatDttm private ZonedDateTime step1EndDttm;
    @JsonFormatDttm private ZonedDateTime step2StrtDttm;
    @JsonFormatDttm private ZonedDateTime step2EndDttm;
    private String step1Status;
    private String step2Status;
    private String statusCd;
    private String trainType;
    private String modelNo;
    private Long currentAttemptId;
    private String requestPath;

    private String packingState;
    @JsonFormatDttm private ZonedDateTime packingStrtDttm;
    @JsonFormatDttm private ZonedDateTime packingEndDttm;

    private Long beforeModelId;
    private Integer bestEpoch;

    private UUID beforeModelUuid;

    public String getStatusName() {
      if (this.statusCd == null || this.statusCd.isBlank()) return null;
      try {
        return (HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.statusCd).getId()
            : TrainStatusType.valueOf(this.statusCd).getText()); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getStep1StatusName() {
      if (this.step1Status == null || this.step1Status.isBlank()) return null;
      try {
        return (HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.step1Status).getId()
            : TrainStatusType.valueOf(this.step1Status).getText()); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.step1Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getStep2StatusName() {
      if (this.step2Status == null || this.step2Status.isBlank()) return null;
      try {
        return (HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.step2Status).getId()
            : TrainStatusType.valueOf(this.step2Status).getText()); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.step2Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getTrainTypeName() {
      if (this.trainType == null || this.trainType.isBlank()) return null;
      try {
        return (HeaderUtil.isEnglishRequest()
            ? TrainType.valueOf(this.trainType).getId()
            : TrainType.valueOf(this.trainType).getText()); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    private String formatDuration(ZonedDateTime start, ZonedDateTime end) {
      if (start == null || end == null) {
        return null;
      }

      long totalSeconds = Math.abs(Duration.between(start, end).getSeconds());

      long hours = totalSeconds / 3600;
      long minutes = (totalSeconds % 3600) / 60;
      long seconds = totalSeconds % 60;

      if (HeaderUtil.isEnglishRequest()) {
        return String.format("%dh %dm %ds", hours, minutes, seconds);
      } else {
        return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
      }
    }

    public String getStep1Duration() {
      return formatDuration(this.step1StrtDttm, this.step1EndDttm);
    }

    public String getStep2Duration() {
      return formatDuration(this.step2StrtDttm, this.step2EndDttm);
    }

    public String getPackingDuration() {
      return formatDuration(this.packingStrtDttm, this.packingEndDttm);
    }
  }

  @Schema(name = "searchReq", description = "모델학습 관리 목록조회 파라미터")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SearchReq {

    private String status;
    private String modelNo;
    // 페이징 파라미터
    private int page = 0;
    private int size = 20;

    public Pageable toPageable() {
      return PageRequest.of(page, size);
    }
  }

  @Schema(name = "addReq", description = "모델학습 관리 등록 파라미터")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class AddReq {

    @NotNull
    @Schema(description = "모델 종류 G1, G2, G3, G4", example = "G1")
    private String modelNo;

    @NotNull
    @Schema(description = "모델학습 실행 여부", example = "false")
    private Boolean isStart;

    @NotNull
    @Schema(description = "학습타입 GENERAL(일반), TRANSFER(전이)", example = "GENERAL")
    private String trainType;

    @Schema(description = "전이학습일때 선택한 모델 id")
    private Long beforeModelId;

    @NotNull
    @Schema(
        description = "하이퍼 파라미터 선택 타입 OPTIMIZED(최적화 파라미터),EXISTING(기존 파라미터),NEW(신규 파라미터)",
        example = "EXISTING")
    private String hyperParamType;

    @Schema(description = "하이퍼파라미터 uuid", example = "57fc9170-64c1-4128-aa7b-0657f08d6d10")
    private UUID hyperUuid;

    HyperParam hyperParam;
    TrainingDataset trainingDataset;
    ModelConfig modelConfig;
  }

  @Schema(name = "addReq", description = "모델학습 관리 등록 파라미터")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class UpdateReq {

    private String requestPath;
    private String responsePath;
    private String tmpFileStatus;
    private ZonedDateTime tmpFileEndDttm;
  }

  @Getter
  @Setter
  public static class TrainingDataset {
    Summary summary;
    List<Long> datasetList;
  }

  @Getter
  @Setter
  public static class Summary {
    @Schema(description = "건물", example = "0")
    private Long buildingCnt;

    @Schema(description = "컨테이너", example = "0")
    private Long containerCnt;

    @Schema(description = "폐기물", example = "0")
    private Long wasteCnt;

    @Schema(description = "도로, 비닐하우스, 밭, 과수원, 초지, 숲, 물, 모재/자갈, 토분(무덤), 일반토지, 기타", example = "0")
    private Long LandCoverCnt;

    @Schema(description = "태양광", example = "0")
    private Long solarPanelCnt;
  }

  @Getter
  @Setter
  public static class ModelConfig {
    @Schema(description = "에폭 횟수", example = "0")
    private Integer epochCnt;

    @Schema(description = "학습데이터셋 비율 Training", example = "0")
    private Float trainingCnt;

    @Schema(description = "학습데이터셋 비율 Validation", example = "0")
    private Float validationCnt;

    @Schema(description = "학습데이터셋 비율 Test", example = "0")
    private Float testCnt;

    @Schema(description = "메모", example = "메모 입니다.")
    private String memo;
  }

  @Schema(name = "모델학습관리 목록", description = "모델학습관리 목록")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class ListDto {

    private Long id;
    private UUID uuid;
    private String modelVer;
    @JsonFormatDttm private ZonedDateTime startDttm;
    @JsonFormatDttm private ZonedDateTime step1StrtDttm;
    @JsonFormatDttm private ZonedDateTime step1EndDttm;
    @JsonFormatDttm private ZonedDateTime step2StrtDttm;
    @JsonFormatDttm private ZonedDateTime step2EndDttm;
    private String step1Status;
    private String step2Status;
    private String statusCd;
    private String trainType;
    private String modelNo;
    private Long currentAttemptId;
    private String requestPath;

    private String packingState;
    private ZonedDateTime packingStrtDttm;
    private ZonedDateTime packingEndDttm;

    private String memo;
    private String userNm;
    private UUID beforeUuid;

    public String getStatusName() {
      if (this.statusCd == null || this.statusCd.isBlank()) return null;
      try {
        return HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.statusCd).getId()
            : TrainStatusType.valueOf(this.statusCd).getText(); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.statusCd; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getStep1StatusName() {
      if (this.step1Status == null || this.step1Status.isBlank()) return null;
      try {
        return HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.step1Status).getId()
            : TrainStatusType.valueOf(this.step1Status).getText(); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.step1Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getStep2StatusName() {
      if (this.step2Status == null || this.step2Status.isBlank()) return null;
      try {
        return HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.step2Status).getId()
            : TrainStatusType.valueOf(this.step2Status).getText(); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.step2Status; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    public String getTrainTypeName() {
      if (this.trainType == null || this.trainType.isBlank()) return null;
      try {
        return HeaderUtil.isEnglishRequest()
            ? TrainType.valueOf(this.trainType).getId()
            : TrainType.valueOf(this.trainType).getText(); // 또는 getName()
      } catch (IllegalArgumentException e) {
        return this.trainType; // 매핑 못하면 코드 그대로 반환(원하면 null 처리)
      }
    }

    private String formatDuration(ZonedDateTime start, ZonedDateTime end) {
      if (start == null || end == null) {
        return null;
      }

      long totalSeconds = Math.abs(Duration.between(start, end).getSeconds());

      long hours = totalSeconds / 3600;
      long minutes = (totalSeconds % 3600) / 60;
      long seconds = totalSeconds % 60;

      if (HeaderUtil.isEnglishRequest()) {
        return String.format("%dh %dm %ds", hours, minutes, seconds);
      } else {
        return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
      }
    }

    public String getStep1Duration() {
      return formatDuration(this.step1StrtDttm, this.step1EndDttm);
    }

    public String getStep2Duration() {
      return formatDuration(this.step2StrtDttm, this.step2EndDttm);
    }

    public String getPackingDuration() {
      return formatDuration(this.packingStrtDttm, this.packingEndDttm);
    }
  }

  @Getter
  @Builder
  @AllArgsConstructor
  public static class ModelProgressStepDto {

    private int step;
    private String status;
    @JsonFormatDttm private ZonedDateTime startTime;
    @JsonFormatDttm private ZonedDateTime endTime;
    private boolean isError;
  }

  @Getter
  @Setter
  public static class CleanupResult {
    // cleanup 대상 전체 파일 수 (삭제 대상 + 유지 파일 포함)
    private int totalCount;

    // 실제로 삭제된 파일 개수
    private int deletedCount;

    // 삭제 실패한 파일 개수
    private int failedCount;

    // 삭제 실패한 파일명 목록
    private List<String> failedFiles;

    // 유지된 파일명 (best epoch 기준)
    private String keptFile;

    // 삭제 될 파일
    private List<String> deleteTargets;
  }

  @Getter
  @Builder
  @AllArgsConstructor
  public static class InProgressModel {

    private String modelNo;
    private UUID uuid;
  }

  @Getter
  @Builder
  @AllArgsConstructor
  public static class ProgressPercent {

    private Long modelId;
    private String jobType;
    private String statusCd;
    private Integer totalEpoch;
    private Integer currentEpoch;
    private Double percent;
  }
}
