package com.kamco.cd.training.postgres.entity;

import com.kamco.cd.training.common.enums.ModelType;
import com.kamco.cd.training.hyperparam.dto.HyperParamDto;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.UuidGenerator;

@Getter
@Setter
@Entity
@Table(
    name = "tb_model_hyper_params",
    indexes = {@Index(name = "idx_hyper_params_hyper_ver", columnList = "hyper_ver")})
public class ModelHyperParamEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "hyper_param_id", nullable = false)
  private Long id;

  @NotNull
  @UuidGenerator
  @Column(name = "uuid", nullable = false, updatable = false)
  private UUID uuid = UUID.randomUUID();

  @Size(max = 50)
  @NotNull
  @Column(name = "hyper_ver", nullable = false, length = 50)
  private String hyperVer;

  // -------------------------
  // Important (Default 반영)
  // -------------------------

  /** Choices: base, large. Default: large */
  @Size(max = 20)
  @NotNull
  @Column(name = "backbone", nullable = false, length = 20)
  private String backbone = "large";

  /** Choices: 256,256 or 512,512. Default: 256,256 */
  @Size(max = 15)
  @NotNull
  @Column(name = "input_size", nullable = false, length = 15)
  private String inputSize = "256,256";

  /** Range: 128~512. Default: 256,256 (H,W or int) */
  @Size(max = 15)
  @NotNull
  @Column(name = "crop_size", nullable = false, length = 15)
  private String cropSize = "256,256";

  /** Range: >=1. Default: 200 */
  @NotNull
  @Column(name = "epoch_cnt", nullable = false)
  private Integer epochCnt = 200;

  /** Range: >=1. Default: 16 (per GPU) */
  @NotNull
  @Column(name = "batch_size", nullable = false)
  private Integer batchSize = 16;

  // -------------------------
  // Model Architecture (Default 반영)
  // -------------------------

  /** Range: 0.0~1.0. Default: 0.3 */
  @NotNull
  @Column(name = "drop_path_rate", nullable = false)
  private Double dropPathRate = 0.3;

  /** Default: -1 (none) */
  @NotNull
  @Column(name = "frozen_stages", nullable = false)
  private Integer frozenStages = -1;

  /** Choices: abs_diff, concat, sum, diff. Default: abs_diff */
  @Size(max = 20)
  @NotNull
  @Column(name = "neck_policy", nullable = false, length = 20)
  private String neckPolicy = "abs_diff";

  /** Default: 512,256,128,64 */
  @Size(max = 50)
  @NotNull
  @Column(name = "decoder_channels", nullable = false, length = 50)
  private String decoderChannels = "512,256,128,64";

  /**
   * Example: 1,1 (large) or 1,10 (base) - 스펙에는 default가 명시가 애매하지만 UI/설명 흐름상 large 기본이므로 1,1로 - 만약
   * base가 기본이면 1,10으로
   */
  @Size(max = 50)
  @NotNull
  @Column(name = "class_weight", nullable = false, length = 50)
  private String classWeight = "1,1";

  /** Default: 24(large), 12(base) → backbone=large 기본이므로 24 */
  @NotNull
  @Column(name = "num_layers", nullable = false)
  private Integer numLayers = 24;

  // -------------------------
  // Loss & Optimization (Default 반영)
  // -------------------------

  /** Range: >0. Default: 6e-5 */
  @NotNull
  @Column(name = "learning_rate", nullable = false)
  private Double learningRate = 6e-5;

  /** Range: >=0. Default: 0.05 */
  @NotNull
  @Column(name = "weight_decay", nullable = false)
  private Double weightDecay = 0.05;

  /** Range: 0.0~1.0. Default: 0.9 */
  @NotNull
  @Column(name = "layer_decay_rate", nullable = false)
  private Double layerDecayRate = 0.9;

  /** Default: true */
  @NotNull
  @Column(name = "ddp_find_unused_params", nullable = false)
  private Boolean ddpFindUnusedParams = true;

  /** Default: 255 */
  @NotNull
  @Column(name = "ignore_index", nullable = false)
  private Integer ignoreIndex = 255;

  // -------------------------
  // Data (Default 반영)
  // -------------------------

  /** Range: >=0. Default: 16 */
  @NotNull
  @Column(name = "train_num_workers", nullable = false)
  private Integer trainNumWorkers = 16;

  /** Range: >=0. Default: 8 */
  @NotNull
  @Column(name = "val_num_workers", nullable = false)
  private Integer valNumWorkers = 8;

  /** Range: >=0. Default: 8 */
  @NotNull
  @Column(name = "test_num_workers", nullable = false)
  private Integer testNumWorkers = 8;

  /** Default: true (from config) */
  @NotNull
  @Column(name = "train_shuffle", nullable = false)
  private Boolean trainShuffle = true;

  /** Default: true */
  @NotNull
  @Column(name = "train_persistent", nullable = false)
  private Boolean trainPersistent = true;

  /** Default: true */
  @NotNull
  @Column(name = "val_persistent", nullable = false)
  private Boolean valPersistent = true;

  // -------------------------
  // Evaluation (Default 반영)
  // -------------------------

  /** Default: mFscore,mIoU */
  @Size(max = 100)
  @NotNull
  @Column(name = "metrics", nullable = false, length = 100)
  private String metrics = "mFscore,mIoU";

  /** Default: changed_fscore */
  @Size(max = 50)
  @NotNull
  @Column(name = "save_best", nullable = false, length = 50)
  private String saveBest = "changed_fscore";

  /** Default: greater */
  @Size(max = 50)
  @NotNull
  @Column(name = "save_best_rule", nullable = false, length = 50)
  private String saveBestRule = "greater";

  /** Default: 1 */
  @NotNull
  @Column(name = "val_interval", nullable = false)
  private Integer valInterval = 1;

  /** Default: 400 */
  @NotNull
  @Column(name = "log_interval", nullable = false)
  private Integer logInterval = 400;

  /** Default: 1 */
  @NotNull
  @Column(name = "vis_interval", nullable = false)
  private Integer visInterval = 1;

  // -------------------------
  // Augmentation (Default 반영)
  // -------------------------

  /** Default: 0.5 */
  @NotNull
  @Column(name = "rot_prob", nullable = false)
  private Double rotProb = 0.5;

  /** Default: 0.5 */
  @NotNull
  @Column(name = "flip_prob", nullable = false)
  private Double flipProb = 0.5;

  /** Default: -20,20 */
  @Size(max = 20)
  @NotNull
  @Column(name = "rot_degree", nullable = false, length = 20)
  private String rotDegree = "-20,20";

  /** Default: 0.5 */
  @NotNull
  @Column(name = "exchange_prob", nullable = false)
  private Double exchangeProb = 0.5;

  /** Default: 10 */
  @NotNull
  @Column(name = "brightness_delta", nullable = false)
  private Integer brightnessDelta = 10;

  /** Default: 0.8,1.2 */
  @Size(max = 20)
  @NotNull
  @Column(name = "contrast_range", nullable = false, length = 20)
  private String contrastRange = "0.8,1.2";

  /** Default: 0.8,1.2 */
  @Size(max = 20)
  @NotNull
  @Column(name = "saturation_range", nullable = false, length = 20)
  private String saturationRange = "0.8,1.2";

  /** Default: 10 */
  @NotNull
  @Column(name = "hue_delta", nullable = false)
  private Integer hueDelta = 10;

  // -------------------------
  // Hardware (스펙상 기본은 있지만, UI에서 예외/옵션이라 했었음)
  // -------------------------

  /** Default: 4 */
  @Column(name = "gpu_cnt")
  private Integer gpuCnt;

  /** Default: 0,1,2,3 */
  @Size(max = 50)
  @Column(name = "gpu_ids", length = 50)
  private String gpuIds;

  /** Default: 1122 */
  @Column(name = "master_port")
  private Integer masterPort;

  // -------------------------
  // Common
  // -------------------------

  @Column(name = "memo")
  private String memo;

  @NotNull
  @ColumnDefault("false")
  @Column(name = "del_yn", nullable = false, length = 1)
  private Boolean delYn = false;

  @NotNull
  @ColumnDefault("CURRENT_TIMESTAMP")
  @Column(name = "created_dttm", nullable = false)
  private ZonedDateTime createdDttm = ZonedDateTime.now();

  @NotNull
  @Column(name = "created_uid", nullable = false)
  private Long createdUid;

  @ColumnDefault("CURRENT_TIMESTAMP")
  @Column(name = "updated_dttm")
  private ZonedDateTime updatedDttm;

  @Column(name = "updated_uid")
  private Long updatedUid;

  @ColumnDefault("CURRENT_TIMESTAMP")
  @Column(name = "last_used_dttm")
  private ZonedDateTime lastUsedDttm;

  @Column(name = "model_type")
  @Enumerated(EnumType.STRING)
  private ModelType modelType;

  @Column(name = "default_param")
  private Boolean isDefault = false;

  @Column(name = "total_use_cnt")
  private Integer totalUseCnt = 0;

  public HyperParamDto.Basic toDto() {
    return new HyperParamDto.Basic(
        this.modelType,
        this.uuid,
        this.hyperVer,
        this.createdDttm,
        this.lastUsedDttm,
        this.totalUseCnt,
        // -------------------------
        // Important
        // -------------------------
        this.backbone,
        this.inputSize,
        this.cropSize,
        this.batchSize,

        // -------------------------
        // Data
        // -------------------------
        this.trainNumWorkers,
        this.valNumWorkers,
        this.testNumWorkers,
        this.trainShuffle,
        this.trainPersistent,
        this.valPersistent,

        // -------------------------
        // Model Architecture
        // -------------------------
        this.dropPathRate,
        this.frozenStages,
        this.neckPolicy,
        this.decoderChannels,
        this.classWeight,

        // -------------------------
        // Loss & Optimization
        // -------------------------
        this.learningRate,
        this.weightDecay,
        this.layerDecayRate,
        this.ddpFindUnusedParams,
        this.ignoreIndex,
        this.numLayers,

        // -------------------------
        // Evaluation
        // -------------------------
        this.metrics,
        this.saveBest,
        this.saveBestRule,
        this.valInterval,
        this.logInterval,
        this.visInterval,

        // -------------------------
        // Augmentation
        // -------------------------
        this.rotProb,
        this.rotDegree,
        this.flipProb,
        this.exchangeProb,
        this.brightnessDelta,
        this.contrastRange,
        this.saturationRange,
        this.hueDelta,

        // -------------------------
        // Memo
        // -------------------------
        this.memo,

        // -------------------------
        // Hardware
        // -------------------------
        this.gpuCnt,
        this.gpuIds,
        this.masterPort,
        this.isDefault);
  }
}
