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

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.kamco.cd.training.common.enums.LearnDataRegister;
import com.kamco.cd.training.common.enums.LearnDataType;
import com.kamco.cd.training.common.enums.ModelType;
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 io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@Slf4j
public class DatasetDto {

  @Schema(name = "Dataset Basic", description = "데이터셋 기본 정보")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class Basic {

    private Long id;
    private UUID uuid;
    private String title;
    private Long roundNo;
    private Integer compareYyyy;
    private Integer targetYyyy;
    private String totalSize;
    private String memo;
    @JsonFormatDttm private ZonedDateTime createdDttm;
    private String status;
    private String statusCd;
    private Boolean deleted;
    private String dataType;
    private String cdClsType;

    public Basic(
        Long id,
        UUID uuid,
        String title,
        Long roundNo,
        Integer compareYyyy,
        Integer targetYyyy,
        Long totalSize,
        String memo,
        ZonedDateTime createdDttm,
        String status,
        Boolean deleted,
        String dataType,
        String cdClsType) {
      this.id = id;
      this.uuid = uuid;
      this.title = title;
      this.roundNo = roundNo;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.totalSize = getTotalSize(totalSize);
      this.memo = memo;
      this.createdDttm = createdDttm;
      this.status = getStatus(status);
      this.statusCd = status;
      this.deleted = deleted;
      this.dataType = dataType;
      this.cdClsType = cdClsType;
    }

    public String getTotalSize(Long totalSize) {
      if (totalSize == null || totalSize <= 0) return "0M";

      double giga = totalSize / (1024.0 * 1024 * 1024);

      if (giga >= 1) {
        return String.format("%.2fG", giga);
      } else {
        double mega = totalSize / (1024.0 * 1024);
        return String.format("%.2fM", mega);
      }
    }

    public String getStatus(String status) {
      LearnDataRegister type = Enums.fromId(LearnDataRegister.class, status);
      boolean english = HeaderUtil.isEnglishRequest();
      return type == null ? null : (english ? type.getId() : type.getText());
    }

    public String getYear() {
      return this.compareYyyy + "-" + this.targetYyyy;
    }

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

  @Schema(name = "Dataset Detail", description = "데이터셋 상세 정보")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class Detail {

    private Long id;
    private String groupTitle;
    private String title;
    private Long roundNo;
    private String totalSize;
    private String memo;
    @JsonFormatDttm private ZonedDateTime createdDttm;
    private String status;
    private Boolean deleted;
  }

  @Schema(name = "DatasetSearchReq", description = "데이터셋 목록 조회 요청")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SearchReq {

    @Schema(description = "구분")
    private String dataType;

    @Schema(description = "제목 (부분 검색)", example = "1차")
    private String title;

    @Schema(description = "페이지 번호 (1부터 시작)", example = "1")
    private int page = 1;

    @Schema(description = "페이지 크기", example = "20")
    private int size = 20;

    public Pageable toPageable() {
      return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdDttm"));
    }
  }

  // TODO 미사용시작
  @Schema(name = "DatasetDetailReq", description = "데이터셋 상세 조회 요청")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class DetailReq {

    @NotNull(message = "데이터셋 ID는 필수입니다")
    @Schema(description = "데이터셋 ID", example = "101")
    private Long datasetId;
  }

  // TODO 미사용 끝

  @Schema(name = "DatasetRegisterReq", description = "데이터셋 등록 요청")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class RegisterReq {

    @NotBlank(message = "제목은 필수입니다")
    @Size(max = 200, message = "제목은 최대 200자까지 입력 가능합니다")
    @Schema(description = "제목", example = "1차 제작")
    private String title;

    @Schema(description = "비교연도 (YYYY)", example = "2023")
    private Integer compareYear;

    @Schema(description = "기준연도 (YYYY)", example = "2024")
    private Integer targetYyyy;

    @Schema(description = "회차", example = "1")
    private Long roundNo;

    @Schema(description = "메모", example = "데이터셋 설명")
    private String memo;
  }

  @Schema(name = "DatasetUpdateReq", description = "데이터셋 수정 요청")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class UpdateReq {

    @Size(max = 200, message = "제목은 최대 200자까지 입력 가능합니다")
    @Schema(description = "제목", example = "1차 제작")
    private String title;

    @Schema(description = "메모", example = "데이터셋 설명")
    private String memo;
  }

  @Schema(name = "DatasetSummaryReq", description = "데이터셋 통계 요약 요청")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SummaryReq {

    @NotNull(message = "데이터셋 ID 목록은 필수입니다")
    @Schema(description = "데이터셋 ID 목록", example = "[101, 105]")
    private List<Long> datasetIds;
  }

  @Schema(name = "DatasetSummary", description = "데이터셋 통계 요약")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class Summary {

    @Schema(description = "총 데이터셋 수", example = "2")
    private int totalDatasets;

    @Schema(description = "총 도엽 수", example = "1500")
    private long totalMapSheets;

    @Schema(description = "총 파일 크기 (bytes)", example = "10737418240")
    private long totalFileSize;

    @Schema(description = "평균 도엽 수", example = "750")
    private double averageMapSheets;
  }

  @Schema(name = "SelectDataSet", description = "데이터셋 선택 리스트")
  @Getter
  @Setter
  @NoArgsConstructor
  public static class SelectDataSet {

    private String modelNo; // G1, G2, G3 모델 타입
    private Long datasetId;
    private UUID uuid;
    private String dataType;
    private String title;
    private Long roundNo;
    private Integer compareYyyy;
    private Integer targetYyyy;
    private String memo;
    @JsonIgnore private Long classCount;
    private Long buildingCnt;
    private Long containerCnt;
    private String dataTypeName;

    private Long wasteCnt;
    private Long landCoverCnt;
    private Long solarPanelCnt;

    public SelectDataSet(
        String modelNo,
        Long datasetId,
        UUID uuid,
        String dataType,
        String title,
        Long roundNo,
        Integer compareYyyy,
        Integer targetYyyy,
        String memo,
        Long classCount) {
      this.modelNo = modelNo;
      this.datasetId = datasetId;
      this.uuid = uuid;
      this.dataType = dataType;
      this.dataTypeName = getDataTypeName(dataType);
      this.title = title;
      this.roundNo = roundNo;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.memo = memo;
      this.classCount = classCount;
      if (modelNo.equals(ModelType.G2.getId())) {
        this.wasteCnt = classCount;
      } else if (modelNo.equals(ModelType.G3.getId())) {
        this.landCoverCnt = classCount;
      } else if (modelNo.equals(ModelType.G4.getId())) {
        this.solarPanelCnt = classCount;
      }
    }

    public SelectDataSet(
        String modelNo,
        Long datasetId,
        UUID uuid,
        String dataType,
        String title,
        Long roundNo,
        Integer compareYyyy,
        Integer targetYyyy,
        String memo,
        Long buildingCnt,
        Long containerCnt) {
      this.modelNo = modelNo;
      this.datasetId = datasetId;
      this.uuid = uuid;
      this.dataType = dataType;
      this.dataTypeName = getDataTypeName(dataType);
      this.title = title;
      this.roundNo = roundNo;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.memo = memo;
      this.buildingCnt = buildingCnt;
      this.containerCnt = containerCnt;
    }

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

    public String getYear() {
      return this.compareYyyy + "-" + this.targetYyyy;
    }
  }

  @Schema(name = "SelectTransferDataSet", description = "전이학습 데이터셋 선택 리스트")
  @Getter
  @Setter
  @NoArgsConstructor
  public static class SelectTransferDataSet {

    private String modelNo; // G1, G2, G3 모델 타입
    private Long datasetId;
    private UUID uuid;
    private String dataType;
    private String title;
    private Long roundNo;
    private Integer compareYyyy;
    private Integer targetYyyy;
    private String memo;
    @JsonIgnore private Long classCount;
    private Integer buildingCnt;
    private Integer containerCnt;
    private String dataTypeName;

    private Long wasteCnt;
    private Long landCoverCnt;

    private String beforeModelNo; // G1, G2, G3 모델 타입
    private Long beforeDatasetId;
    private UUID beforeUuid;
    private String beforeDataType;
    private String beforeTitle;
    private Long beforeRoundNo;
    private Integer beforeCompareYyyy;
    private Integer beforeTargetYyyy;
    private String beforeMemo;

    @JsonIgnore private Long beforeClassCount;

    private Integer beforeBuildingCnt;
    private Integer beforeContainerCnt;
    private String beforeDataTypeName;

    private Long beforeWasteCnt;
    private Long beforeLandCoverCnt;

    public SelectTransferDataSet(
        // 현재
        String modelNo,
        Long datasetId,
        UUID uuid,
        String dataType,
        String title,
        Long roundNo,
        Integer compareYyyy,
        Integer targetYyyy,
        String memo,
        Long classCount,

        // 이전(before)
        String beforeModelNo,
        Long beforeDatasetId,
        UUID beforeUuid,
        String beforeDataType,
        String beforeTitle,
        Long beforeRoundNo,
        Integer beforeCompareYyyy,
        Integer beforeTargetYyyy,
        String beforeMemo,
        Long beforeClassCount) {
      // 현재
      this.modelNo = modelNo;
      this.datasetId = datasetId;
      this.uuid = uuid;
      this.dataType = dataType;
      this.dataTypeName = getDataTypeName(dataType);
      this.title = title;
      this.roundNo = roundNo;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.memo = memo;
      this.classCount = classCount;

      if (modelNo != null && modelNo.equals(ModelType.G2.getId())) {
        this.wasteCnt = classCount;
      } else if (modelNo != null && modelNo.equals(ModelType.G3.getId())) {
        this.landCoverCnt = classCount;
      }

      // 이전(before)
      this.beforeModelNo = beforeModelNo;
      this.beforeDatasetId = beforeDatasetId;
      this.beforeUuid = beforeUuid;
      this.beforeDataType = beforeDataType;
      this.beforeDataTypeName = getDataTypeName(beforeDataType);
      this.beforeTitle = beforeTitle;
      this.beforeRoundNo = beforeRoundNo;
      this.beforeCompareYyyy = beforeCompareYyyy;
      this.beforeTargetYyyy = beforeTargetYyyy;
      this.beforeMemo = beforeMemo;
      this.beforeClassCount = beforeClassCount;

      if (beforeModelNo != null && beforeModelNo.equals(ModelType.G2.getId())) {
        this.beforeWasteCnt = beforeClassCount;
      } else if (beforeModelNo != null && beforeModelNo.equals(ModelType.G3.getId())) {
        this.beforeLandCoverCnt = beforeClassCount;
      }
    }

    public SelectTransferDataSet(
        // 현재
        String modelNo,
        Long datasetId,
        UUID uuid,
        String dataType,
        String title,
        Long roundNo,
        Integer compareYyyy,
        Integer targetYyyy,
        String memo,
        Integer buildingCnt,
        Integer containerCnt,

        // 이전(before)
        String beforeModelNo,
        Long beforeDatasetId,
        UUID beforeUuid,
        String beforeDataType,
        String beforeTitle,
        Long beforeRoundNo,
        Integer beforeCompareYyyy,
        Integer beforeTargetYyyy,
        String beforeMemo,
        Integer beforeBuildingCnt,
        Integer beforeContainerCnt) {
      // 현재
      this.modelNo = modelNo;
      this.datasetId = datasetId;
      this.uuid = uuid;
      this.dataType = dataType;
      this.dataTypeName = getDataTypeName(dataType);
      this.title = title;
      this.roundNo = roundNo;
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.memo = memo;
      this.buildingCnt = buildingCnt;
      this.containerCnt = containerCnt;

      // 이전(before)
      this.beforeModelNo = beforeModelNo;
      this.beforeDatasetId = beforeDatasetId;
      this.beforeUuid = beforeUuid;
      this.beforeDataType = beforeDataType;
      this.beforeDataTypeName = getDataTypeName(beforeDataType);
      this.beforeTitle = beforeTitle;
      this.beforeRoundNo = beforeRoundNo;
      this.beforeCompareYyyy = beforeCompareYyyy;
      this.beforeTargetYyyy = beforeTargetYyyy;
      this.beforeMemo = beforeMemo;
      this.beforeBuildingCnt = beforeBuildingCnt;
      this.beforeContainerCnt = beforeContainerCnt;
    }

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

    public String getYear() {
      return this.compareYyyy + "-" + this.targetYyyy;
    }

    public String getBeforeYear() {
      if (this.beforeCompareYyyy == null || this.beforeTargetYyyy == null) {
        return null;
      }
      return this.beforeCompareYyyy + "-" + this.beforeTargetYyyy;
    }
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class DatasetReq {
    String modelNo;
    String dataType;
    UUID uuid;
    Long id;
    List<Long> ids;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class AddReq {
    private String fileName;
    private String filePath;
    private Long fileSize;
    private String memo;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  public static class DatasetMngRegDto {
    private String uid;
    private String dataType;
    private Integer compareYyyy;
    private Integer targetYyyy;
    private Long roundNo;
    private String title;
    private String memo;
    private Long totalSize;
    private Long totalObjectCount;
    private String datasetPath;
    private String cdClsType;
  }

  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class AddDeliveriesReq {
    @Schema(description = "경로", example = "/")
    private String filePath;

    @Schema(description = "제목", example = "")
    private String title;

    @Schema(description = "메모", example = "")
    private String memo;

    @Schema(description = "비교년도", example = "")
    private Integer compareYyyy;

    @Schema(description = "기준년도", example = "")
    private Integer targetYyyy;

    @Schema(description = "회차", example = "")
    private Long roundNo;
  }
}
