package com.kamco.cd.kamcoback.inference.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kamco.cd.kamcoback.common.enums.DetectionClassification;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

public class InferenceResultDto {

  @Schema(name = "InferenceResultBasic", description = "분석결과 기본 정보")
  @Getter
  public static class Basic {

    private Long id;
    private String dataName;
    private Long mapSheepNum;
    private Long detectingCnt;
    @JsonFormatDttm private ZonedDateTime analStrtDttm;
    @JsonFormatDttm private ZonedDateTime analEndDttm;
    private Long analSec;
    private String analState;

    public Basic(
        Long id,
        String dataName,
        Long mapSheepNum,
        Long detectingCnt,
        ZonedDateTime analStrtDttm,
        ZonedDateTime analEndDttm,
        Long analSec,
        String analState) {
      this.id = id;
      this.dataName = dataName;
      this.mapSheepNum = mapSheepNum;
      this.detectingCnt = detectingCnt;
      this.analStrtDttm = analStrtDttm;
      this.analEndDttm = analEndDttm;
      this.analSec = analSec;
      this.analState = analState;
    }
  }

  @Schema(name = "AnalysisResultList", description = "분석결과 목록")
  @Getter
  public static class AnalResList {

    private Long id;
    private String analTitle;
    private String analMapSheet;
    private Long detectingCnt;
    @JsonFormatDttm private ZonedDateTime analStrtDttm;
    @JsonFormatDttm private ZonedDateTime analEndDttm;
    private Long analSec;
    private Long analPredSec;
    private String analState;
    private String analStateNm;
    private String gukyuinUsed;

    public AnalResList(
        Long id,
        String analTitle,
        String analMapSheet,
        Long detectingCnt,
        ZonedDateTime analStrtDttm,
        ZonedDateTime analEndDttm,
        Long analSec,
        Long analPredSec,
        String analState,
        String analStateNm,
        String gukyuinUsed) {
      this.id = id;
      this.analTitle = analTitle;
      this.analMapSheet = analMapSheet;
      this.detectingCnt = detectingCnt;
      this.analStrtDttm = analStrtDttm;
      this.analEndDttm = analEndDttm;
      this.analSec = analSec;
      this.analPredSec = analPredSec;
      this.analState = analState;
      this.analStateNm = analStateNm;
      this.gukyuinUsed = gukyuinUsed;
    }
  }

  @Schema(name = "AnalysisResultSummary", description = "분석결과 요약정보")
  @Getter
  public static class AnalResSummary {

    private Long id;
    private String analTitle;
    private String modelInfo;
    private Integer targetYyyy;
    private Integer compareYyyy;
    private String analMapSheet;
    @JsonFormatDttm private ZonedDateTime analStrtDttm;
    @JsonFormatDttm private ZonedDateTime analEndDttm;
    private Long analSec;
    private Long analPredSec;
    private String resultUrl;
    private Long detectingCnt;
    private Double accuracy;
    private String analState;
    private String analStateNm;

    public AnalResSummary(
        Long id,
        String analTitle,
        String modelInfo,
        Integer targetYyyy,
        Integer compareYyyy,
        String analMapSheet,
        ZonedDateTime analStrtDttm,
        ZonedDateTime analEndDttm,
        Long analSec,
        Long analPredSec,
        String resultUrl,
        Long detectingCnt,
        Double accuracy,
        String analState,
        String analStateNm) {
      this.id = id;
      this.analTitle = analTitle;
      this.modelInfo = modelInfo;
      this.targetYyyy = targetYyyy;
      this.compareYyyy = compareYyyy;
      this.analMapSheet = analMapSheet;
      this.analStrtDttm = analStrtDttm;
      this.analEndDttm = analEndDttm;
      this.analSec = analSec;
      this.analPredSec = analPredSec;
      this.resultUrl = resultUrl;
      this.detectingCnt = detectingCnt;
      this.accuracy = accuracy;
      this.analState = analState;
      this.analStateNm = analStateNm;
    }
  }

  @Getter
  public static class Dashboard {

    String classAfterCd;
    String classAfterName;
    Long classAfterCnt;

    public Dashboard(String classAfterCd, Long classAfterCnt) {
      this.classAfterCd = classAfterCd;
      this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
      this.classAfterCnt = classAfterCnt;
    }
  }

  @Getter
  public static class Detail {

    AnalResSummary summary;
    List<Dashboard> dashboard;
    Long totalCnt;

    public Detail(AnalResSummary summary, List<Dashboard> dashboard, Long totalCnt) {
      this.summary = summary;
      this.dashboard = dashboard;
      this.totalCnt = totalCnt;
    }
  }

  // 분석 상세 ROW
  @Getter
  @AllArgsConstructor
  public static class DetailListEntity {

    private Uid code;
    private Double detectionScore;
    private Clazzes compare;
    private Clazzes target;
    private MapSheet mapSheet;
    private Coordinate center;
    @JsonFormatDttm private ZonedDateTime updatedDttm;

    public DetailListEntity(
        UUID uuid,
        Double detectionScore,
        Clazzes compare,
        Clazzes target,
        MapSheet mapSheet,
        Coordinate center,
        ZonedDateTime updatedDttm) {
      this.code = new Uid(uuid);
      this.detectionScore = detectionScore;
      this.compare = compare;
      this.target = target;
      this.mapSheet = mapSheet;
      this.center = center;
      this.updatedDttm = updatedDttm;
    }
  }

  @Getter
  @AllArgsConstructor
  public static class Uid {

    private String shortCode;
    private String code;

    public Uid(UUID uuid) {
      if (uuid != null) {
        this.shortCode = uuid.toString().substring(0, 8).toUpperCase();
        this.code = uuid.toString();
      }
    }
  }

  // MAP NO
  @Getter
  @AllArgsConstructor
  public static class MapSheet {

    private String number;
    private String name;
  }

  // classification info
  @Getter
  public static class Clazz {

    private String code;
    private String name;
    @JsonIgnore private Double score;

    public Clazz(String code, Double score) {
      this.code = code;
      this.score = score;
      this.name = DetectionClassification.fromString(code).getDesc();
    }

    public Clazz(String code) {
      this.code = code;
      this.name = DetectionClassification.fromString(code).getDesc();
    }
  }

  // classification info
  @Getter
  public static class Clazzes {

    private DetectionClassification code;
    private String name;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Double score;

    private Integer order;

    public Clazzes(DetectionClassification classification, Double score) {
      this.code = classification;
      this.name = classification.getDesc();
      this.order = classification.getOrder();
      this.score = score;
    }

    public Clazzes(DetectionClassification classification) {
      this.code = classification;
      this.name = classification.getDesc();
      this.order = classification.getOrder();
    }
  }

  // 좌표 정보 point
  @Getter
  public static class Coordinate {

    private Double lon; // 경도(Longitude)
    private Double lat; // 위도(Latitude)
    private String srid; // Spatial Reference ID의 약자로, 데이터베이스에서 좌표계를 식별하는 고유 번호  추후enum으로

    public Coordinate(Double lon, Double lat) {
      this.lon = lon;
      this.lat = lat;
      this.srid = "EPSG:4326";
    }
  }

  @Getter
  public static class Geom {

    Integer compareYyyy;
    Integer targetYyyy;
    String classBeforeCd;
    String classBeforeName;
    Double classBeforeProb;
    String classAfterCd;
    String classAfterName;
    Double classAfterProb;
    Long mapSheetNum;
    Geometry gemo;
    Geometry geomCenter;

    public Geom(
        Integer compareYyyy,
        Integer targetYyyy,
        String classBeforeCd,
        Double classBeforeProb,
        String classAfterCd,
        Double classAfterProb,
        Long mapSheetNum,
        Geometry gemo,
        Geometry geomCenter) {
      this.compareYyyy = compareYyyy;
      this.targetYyyy = targetYyyy;
      this.classBeforeCd = classBeforeCd;
      this.classBeforeName = DetectionClassification.fromString(classBeforeCd).getDesc();
      this.classBeforeProb = classBeforeProb;
      this.classAfterCd = classAfterCd;
      this.classAfterName = DetectionClassification.fromString(classAfterCd).getDesc();
      this.classAfterProb = classAfterProb;
      this.mapSheetNum = mapSheetNum;
      this.gemo = gemo;
      this.geomCenter = geomCenter;
    }
  }

  @Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SearchReq {

    // 검색 조건
    private String statCode;
    private String title;

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

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

  @Schema(name = "InferenceResultSearchReq", description = "분석결과 목록 요청 정보")
  @Getter
  @Setter
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SearchGeoReq {

    // 기준년도
    private String targetClass;
    // 비교년도
    private String compareClass;
    // 분석도엽
    private List<Long> mapSheetNum;

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

    public Pageable toPageable() {
      if (sort != null && !sort.isEmpty()) {
        String[] sortParams = sort.split(",");
        String property = sortParams[0];
        Sort.Direction direction =
            sortParams.length > 1 ? Sort.Direction.fromString(sortParams[1]) : Sort.Direction.ASC;
        return PageRequest.of(page, size, Sort.by(direction, property));
      }
      return PageRequest.of(page, size);
    }
  }
}
