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

import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.training.common.enums.LearnDataType;
import com.kamco.cd.training.common.enums.TrainStatusType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.utils.enums.Enums;
import com.kamco.cd.training.common.utils.interfaces.JsonFormatDttm;
import com.kamco.cd.training.dataset.dto.DatasetDto.SelectTransferDataSet;
import io.swagger.v3.oas.annotations.media.Schema;
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;

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

    private Long modelId;
    private UUID uuid;
    private String modelNo;
    private String modelVer;
    @JsonFormatDttm private ZonedDateTime step1StrtDttm;
    @JsonFormatDttm private ZonedDateTime step2EndDttm;
    private String statusCd;
    private String trainType;
    private UUID beforeUuid;

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

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

    private String formatDuration(ZonedDateTime start, ZonedDateTime end) {
      if (end == null) {
        end = ZonedDateTime.now();
      }

      if (start == 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;

      return String.format("%d시간 %d분 %d초", hours, minutes, seconds);
    }

    public String getStepAllDuration() {
      return formatDuration(this.step1StrtDttm, this.step2EndDttm);
    }
  }

  @Schema(name = "모델학습관리 목록", description = "모델학습관리 목록")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class HyperSummary {
    private UUID uuid;
    private Long hyperParamId;
    private String hyperVer;
    private String backbone;
    private String inputSize;
    private String cropSize;
    private Integer batchSize;
  }

  @Schema(name = "모델학습관리 전이 하이파라미터", description = "모델학습관리 전이 하이파라미터")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class TransferHyperSummary {
    private UUID uuid;
    private Long hyperParamId;
    private String hyperVer;
    private String backbone;
    private String inputSize;
    private String cropSize;
    private Integer batchSize;
    private UUID beforeUuid;
    private Long beforeHyperParamId;
    private String beforeHyperVer;
    private String beforeBackbone;
    private String beforeInputSize;
    private String beforeCropSize;
    private Integer beforeBatchSize;
  }

  @Schema(name = "선택한 데이터셋 목록", description = "선택한 데이터셋 목록")
  @Getter
  @Setter
  @NoArgsConstructor
  public static class MappingDataset {
    private Long modelId;
    private Long datasetId;
    private String dataType;
    private Integer compareYyyy;
    private Integer targetYyyy;
    private Long roundNo;
    private String dataTypeName;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long buildingCnt;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long containerCnt;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long wasteCnt;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long landCoverCnt;

    public MappingDataset(
        Long modelId,
        Long datasetId,
        String dataType,
        Integer compareYyyy,
        Integer targetYyyy,
        Long roundNo,
        Long buildingCnt,
        Long containerCnt,
        Long wasteCnt,
        Long landCoverCnt) {
      this.modelId = modelId;
      this.datasetId = datasetId;
      this.dataType = dataType;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.roundNo = roundNo;
      this.buildingCnt = buildingCnt;
      this.containerCnt = containerCnt;
      this.wasteCnt = wasteCnt;
      this.landCoverCnt = landCoverCnt;
      this.dataTypeName = getDataTypeName(this.dataType);
    }

    public String getDataTypeName(String groupTitleCd) {
      LearnDataType type = Enums.fromId(LearnDataType.class, groupTitleCd);
      return type == null ? null : type.getText();
    }
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class TransferDetailDto {
    private ModelConfigDto.TransferBasic etcConfig;
    private TransferHyperSummary modelTrainHyper;
    private List<SelectTransferDataSet> modelTrainDataset;
    //    private List<SelectDataSet> beforeTrainDataset;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class ModelTrainMetrics {
    private Integer epoch;
    private Long iteration;
    private Double loss;
    private Double lr;
    private Float durationTime;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class ModelValidationMetrics {

    private Integer epoch;
    private Float aAcc;
    private Float mFscore;
    private Float mPrecision;
    private Float mRecall;
    private Float mIou;
    private Float mAcc;
    private Float changedFscore;
    private Float changedPrecision;
    private Float changedRecall;
    private Float unchangedFscore;
    private Float unchangedPrecision;
    private Float unchangedRecall;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class ModelTestMetrics {
    private String model;
    private Long tp;
    private Long fp;
    private Long fn;
    private Float precision;
    private Float recall;
    private Float f1Score;
    private Float accuracy;
    private Float iou;
    private Long detectionCount;
    private Long gtCount;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class ModelBestEpoch {
    private Integer epoch;
    private Double loss;
    private Float f1Score;
    private Float precision;
    private Float recall;
    private Float iou;
    private Float accuracy;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class ModelFileInfo {
    private Boolean fileExistsYn;
    private String fileName;
  }
}
