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.HeaderUtil;
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 step1EndDttm;
    @JsonFormatDttm private ZonedDateTime step2StrtDttm;
    @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 HeaderUtil.isEnglishRequest()
            ? TrainStatusType.valueOf(this.statusCd).getId()
            : 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 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 getStepAllDuration() {
      if (this.step2EndDttm != null) {
        // step1 + step2 실제 소요시간 합산
        long step1Seconds = 0;
        long step2Seconds = 0;

        if (this.step1StrtDttm != null && this.step1EndDttm != null) {
          step1Seconds =
              Math.abs(Duration.between(this.step1StrtDttm, this.step1EndDttm).getSeconds());
        }
        if (this.step2StrtDttm != null && this.step2EndDttm != null) {
          step2Seconds =
              Math.abs(Duration.between(this.step2StrtDttm, this.step2EndDttm).getSeconds());
        }

        long totalSeconds = step1Seconds + step2Seconds;
        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);
        }
      } else {
        // step2 없으면 step1만
        return formatDuration(this.step1StrtDttm, this.step1EndDttm);
      }
    }
  }

  @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;

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

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

      this.dataTypeName = getDataTypeName(this.dataType);
    }

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

  private static Long toNullIfZero(Long value) {
    return (value == null || value == 0L) ? null : value;
  }

  @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;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  @Schema(name = "ZipFileListResponse", description = "ZIP 파일 목록 조회 응답")
  public static class ZipFileListResponse {

    @Schema(description = "모델 UUID", example = "2c5cdd07-5b9b-44ad-a063-8dead628b45f")
    private String modelUuid;

    @Schema(description = "모델 번호", example = "G2")
    private String modelNo;

    @Schema(description = "현재 모델 버전", example = "G2_000001")
    private String modelVer;

    @Schema(
        description = "기본 경로",
        example = "/home/kcomu/data/response/2c5cdd07-5b9b-44ad-a063-8dead628b45f")
    private String basePath;

    @Schema(description = "ZIP 파일 목록")
    private List<ZipFileInfo> zipFiles;

    @Schema(description = "총 파일 개수", example = "3")
    private Integer totalFiles;

    @Schema(description = "전체 파일 크기 (bytes)", example = "4048640000")
    private Long totalSize;

    @Schema(description = "전체 파일 크기 (포맷)", example = "3.77 GB")
    private String totalSizeFormatted;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  @Schema(name = "ZipFileInfo", description = "ZIP 파일 상세 정보")
  public static class ZipFileInfo {

    @Schema(description = "파일명", example = "G2.G2_000001.{uuid}.zip")
    private String fileName;

    @Schema(description = "파일 전체 경로")
    private String filePath;

    @Schema(description = "모델 버전", example = "G2_000001")
    private String version;

    @Schema(description = "파일 크기 (bytes)", example = "1349516895")
    private Long fileSize;

    @Schema(description = "파일 크기 (포맷)", example = "1.26 GB")
    private String fileSizeFormatted;

    @Schema(description = "최종 수정 시간", example = "2026-03-10T23:19:24.347+09:00")
    @JsonFormatDttm
    private ZonedDateTime lastModified;

    @Schema(description = "현재 모델 버전 여부", example = "true")
    private Boolean isCurrent;

    @Schema(description = "다운로드 URL")
    private String downloadUrl;
  }
}
