package com.kamco.cd.training.train.util;

import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import lombok.experimental.UtilityClass;

/**
 * 학습 진행률 계산 유틸리티 기존 DB 컬럼만을 활용하여 진행률을 실시간 계산
 *
 * <p>상태전의 STATUS 가이드 기준: - Step1 (학습): TRAIN jobType → 0% ~ 50% - Step2 (테스트): EVAL jobType → 50% ~
 * 100%
 */
@UtilityClass
public class TrainingProgressCalculator {

  /**
   * 학습 진행률 계산 (Step 구분)
   *
   * @param statusCd 작업 상태 (QUEUED/RUNNING/SUCCESS/FAILED/STOPPED/CANCELED)
   * @param currentEpoch 현재 Epoch
   * @param totalEpoch 전체 Epoch
   * @param startedDttm 시작 시각
   * @param finishedDttm 종료 시각
   * @param jobType 작업 타입 (TRAIN/EVAL)
   * @param step1State Step1 상태 (READY/IN_PROGRESS/COMPLETED/STOPPED/ERROR)
   * @param step2State Step2 상태 (READY/IN_PROGRESS/COMPLETED/STOPPED/ERROR)
   * @return 진행률 (0.00 ~ 100.00)
   */
  public static double calculateProgress(
      String statusCd,
      Integer currentEpoch,
      Integer totalEpoch,
      ZonedDateTime startedDttm,
      ZonedDateTime finishedDttm,
      String jobType,
      String step1State,
      String step2State) {
    if (statusCd == null) {
      return 0.0;
    }

    // Step별 진행률 계산
    boolean isStep1 = "TRAIN".equals(jobType);
    boolean isStep2 = "EVAL".equals(jobType) || "TEST".equals(jobType);

    // Step1 완료, Step2 완료 여부 확인
    boolean isStep1Completed = "COMPLETED".equals(step1State);
    boolean isStep2Completed = "COMPLETED".equals(step2State);

    switch (statusCd) {
      case "QUEUED":
        // Step1 대기 중: 0%, Step2 대기 중: 50%
        return isStep2 ? 50.0 : 0.0;

      case "RUNNING":
        return calculateRunningProgress(
            currentEpoch, totalEpoch, startedDttm, isStep1, isStep2, isStep1Completed);

      case "SUCCESS":
        // Step1 성공: 50%, Step2 성공: 100%
        if (isStep2 || isStep2Completed) {
          return 100.0;
        } else if (isStep1 || isStep1Completed) {
          return 50.0;
        }
        return 100.0; // 기본값

      case "FAILED":
      case "STOPPED":
      case "CANCELED":
        // 실패/취소된 경우 마지막 진행률 반환
        return calculateRunningProgress(
            currentEpoch, totalEpoch, startedDttm, isStep1, isStep2, isStep1Completed);

      default:
        return 0.0;
    }
  }

  /**
   * 실행 중인 학습의 진행률 계산 (Step 구분)
   *
   * <p>Step1 (학습): 0% ~ 50% - PREPARING: 0% ~ 2.5% - CONTAINER_STARTING: 2.5% ~ 5% - DATA_LOADING:
   * 5% ~ 7.5% - TRAINING: 7.5% ~ 42.5% (35% 비중) - PROCESSING: 42.5% ~ 50%
   *
   * <p>Step2 (테스트): 50% ~ 100% - PREPARING: 50% ~ 52.5% - CONTAINER_STARTING: 52.5% ~ 55% -
   * DATA_LOADING: 55% ~ 57.5% - TESTING: 57.5% ~ 92.5% (35% 비중) - PROCESSING: 92.5% ~ 100%
   */
  private static double calculateRunningProgress(
      Integer currentEpoch,
      Integer totalEpoch,
      ZonedDateTime startedDttm,
      boolean isStep1,
      boolean isStep2,
      boolean isStep1Completed) {

    // Step 기준 계산
    double baseProgress = 0.0;
    double maxProgress = 50.0;

    if (isStep2 || isStep1Completed) {
      // Step2 진행 중 또는 Step1 완료 후
      baseProgress = 50.0;
      maxProgress = 100.0;
    } else if (isStep1) {
      // Step1 진행 중
      baseProgress = 0.0;
      maxProgress = 50.0;
    }

    double stepRange = maxProgress - baseProgress; // 50%

    // 시작 직후 (Epoch 정보 없음)
    if (currentEpoch == null || totalEpoch == null || totalEpoch == 0) {
      return baseProgress + estimateInitialPhaseProgress(startedDttm, stepRange);
    }

    // Epoch 0 또는 1 (학습 시작 단계)
    if (currentEpoch <= 1) {
      return baseProgress + (stepRange * 0.15); // 7.5% (Step1) 또는 57.5% (Step2)
    }

    // 학습 진행 중 (15% ~ 85% of step range)
    double epochProgress = (double) currentEpoch / totalEpoch;
    double trainingProgress = 0.15 + (epochProgress * 0.70); // 15% ~ 85%

    // 학습 완료 (마지막 Epoch)
    if (currentEpoch >= totalEpoch) {
      // 85% ~ 100% 사이로 추정
      return baseProgress + (stepRange * 0.90);
    }

    return baseProgress + (stepRange * trainingProgress);
  }

  /**
   * 초기 단계 진행률 추정 (시작 시간 기반)
   *
   * @param startedDttm 시작 시각
   * @param stepRange Step 범위 (50%)
   * @return 초기 단계 진행률 (0 ~ 0.15 비율)
   */
  private static double estimateInitialPhaseProgress(ZonedDateTime startedDttm, double stepRange) {
    if (startedDttm == null) {
      return stepRange * 0.05; // 5% of step range
    }

    long elapsedSeconds = ChronoUnit.SECONDS.between(startedDttm, ZonedDateTime.now());

    // 시작 후 30초 이내: PREPARING (0% ~ 5% of step)
    if (elapsedSeconds < 30) {
      return Math.min(stepRange * 0.05, (elapsedSeconds / 30.0) * stepRange * 0.05);
    }
    // 30초 ~ 60초: CONTAINER_STARTING (5% ~ 10% of step)
    else if (elapsedSeconds < 60) {
      return stepRange * 0.05 + ((elapsedSeconds - 30) / 30.0) * stepRange * 0.05;
    }
    // 60초 이상: DATA_LOADING (10% ~ 15% of step)
    else {
      return Math.min(
          stepRange * 0.15, stepRange * 0.10 + ((elapsedSeconds - 60) / 60.0) * stepRange * 0.05);
    }
  }

  /**
   * 현재 Phase 추정
   *
   * @param statusCd 작업 상태
   * @param currentEpoch 현재 Epoch
   * @param totalEpoch 전체 Epoch
   * @param startedDttm 시작 시각
   * @param jobType 작업 타입 (TRAIN/EVAL)
   * @return 현재 Phase
   */
  public static String estimateCurrentPhase(
      String statusCd,
      Integer currentEpoch,
      Integer totalEpoch,
      ZonedDateTime startedDttm,
      String jobType) {
    if ("SUCCESS".equals(statusCd)) {
      return "COMPLETED";
    }
    if ("FAILED".equals(statusCd)) {
      return "FAILED";
    }
    if ("STOPPED".equals(statusCd) || "CANCELED".equals(statusCd)) {
      return "CANCELED";
    }
    if ("QUEUED".equals(statusCd)) {
      return "QUEUED";
    }

    // RUNNING 상태
    boolean isStep2 = "EVAL".equals(jobType) || "TEST".equals(jobType);
    String prefix = isStep2 ? "STEP2_" : "STEP1_";

    if (currentEpoch == null || totalEpoch == null) {
      if (startedDttm == null) {
        return prefix + "PREPARING";
      }
      long elapsed = ChronoUnit.SECONDS.between(startedDttm, ZonedDateTime.now());
      if (elapsed < 30) return prefix + "PREPARING";
      if (elapsed < 60) return prefix + "CONTAINER_STARTING";
      return prefix + "DATA_LOADING";
    }

    if (currentEpoch >= totalEpoch) {
      return prefix + "PROCESSING_RESULTS";
    }

    return prefix + "TRAINING";
  }

  /** 경과 시간 계산 (초) */
  public static Long calculateElapsedSeconds(ZonedDateTime startedDttm) {
    if (startedDttm == null) {
      return null;
    }
    return ChronoUnit.SECONDS.between(startedDttm, ZonedDateTime.now());
  }

  /** 예상 남은 시간 계산 (초) */
  public static Long estimateRemainingSeconds(double progressPercent, Long elapsedSeconds) {
    if (elapsedSeconds == null || progressPercent <= 0.0) {
      return null;
    }

    double remaining = (100.0 - progressPercent) / progressPercent;
    return (long) (elapsedSeconds * remaining);
  }

  /**
   * 상태 메시지 생성
   *
   * @param phase 현재 Phase
   * @param currentEpoch 현재 Epoch
   * @param totalEpoch 전체 Epoch
   * @return 상태 메시지
   */
  public static String generateProgressMessage(
      String phase, Integer currentEpoch, Integer totalEpoch) {
    if (phase == null) {
      return "대기중";
    }

    // Step 구분
    boolean isStep1 = phase.startsWith("STEP1_");
    boolean isStep2 = phase.startsWith("STEP2_");
    String stepName = isStep1 ? "학습" : isStep2 ? "테스트" : "";

    switch (phase) {
      case "QUEUED":
        return "학습 작업이 큐에 등록되었습니다.";

      case "STEP1_PREPARING":
        return "학습 준비 중입니다.";
      case "STEP1_CONTAINER_STARTING":
        return "학습용 Docker 컨테이너를 시작하는 중입니다.";
      case "STEP1_DATA_LOADING":
        return "학습 데이터를 로딩하는 중입니다.";
      case "STEP1_TRAINING":
        if (currentEpoch != null && totalEpoch != null) {
          return String.format("학습 진행 중 (Epoch %d/%d)", currentEpoch, totalEpoch);
        }
        return "학습 진행 중";
      case "STEP1_PROCESSING_RESULTS":
        return "학습 결과를 처리하는 중입니다.";

      case "STEP2_PREPARING":
        return "테스트 준비 중입니다.";
      case "STEP2_CONTAINER_STARTING":
        return "테스트용 Docker 컨테이너를 시작하는 중입니다.";
      case "STEP2_DATA_LOADING":
        return "테스트 데이터를 로딩하는 중입니다.";
      case "STEP2_TRAINING":
        if (currentEpoch != null && totalEpoch != null) {
          return String.format("테스트 진행 중 (Epoch %d/%d)", currentEpoch, totalEpoch);
        }
        return "테스트 진행 중";
      case "STEP2_PROCESSING_RESULTS":
        return "테스트 결과를 처리하는 중입니다.";

      case "COMPLETED":
        return "학습이 성공적으로 완료되었습니다.";
      case "FAILED":
        return "학습이 실패했습니다.";
      case "CANCELED":
        return "학습이 취소되었습니다.";

      default:
        return stepName + " 진행 중";
    }
  }
}
