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

import com.kamco.cd.training.model.dto.ModelTrainMngDto;
import com.kamco.cd.training.postgres.core.ModelTrainJobCoreService;
import com.kamco.cd.training.postgres.core.ModelTrainMngCoreService;
import com.kamco.cd.training.train.dto.ModelTrainJobQueuedEvent;
import com.kamco.cd.training.train.dto.TrainRunRequest;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TestJobService {
  private final ModelTrainJobCoreService modelTrainJobCoreService;
  private final ModelTrainMngCoreService modelTrainMngCoreService;
  private final DockerTrainService dockerTrainService;
  private final ApplicationEventPublisher eventPublisher;
  private final DataSetCountersService dataSetCounters;

  /**
   * 실행 예약 (QUEUE 등록)
   *
   * @param modelId
   * @param uuid
   * @param epoch
   * @return
   */
  @Transactional
  public Long enqueue(Long modelId, UUID uuid, int epoch) {

    // 마스터 확인
    modelTrainMngCoreService.findModelById(modelId);

    // 폴더 카운트
    dataSetCounters.getCount(modelId);

    // best epoch 업데이트
    modelTrainMngCoreService.updateModelMasterBestEpoch(modelId, epoch);

    // 파라미터 조회
    TrainRunRequest trainRunRequest = modelTrainMngCoreService.findTrainRunRequest(modelId);

    Map<String, Object> params = new java.util.LinkedHashMap<>();
    params.put("jobType", "EVAL");
    params.put("uuid", String.valueOf(uuid));
    params.put("epoch", epoch);
    params.put("datasetFolder", trainRunRequest.getDatasetFolder());
    params.put("outputFolder", trainRunRequest.getOutputFolder());
    params.put("reqTmpYn", trainRunRequest.getReqTmpYn());

    int nextAttemptNo = modelTrainJobCoreService.findMaxAttemptNo(modelId) + 1;

    Long jobId =
        modelTrainJobCoreService.createQueuedJob(
            modelId, nextAttemptNo, params, ZonedDateTime.now());

    // test training run 테이블에 적재하기
    modelTrainJobCoreService.insertModelTestTrainingRun(modelId, jobId, epoch);

    eventPublisher.publishEvent(new ModelTrainJobQueuedEvent(jobId));
    return jobId;
  }

  /**
   * 취소
   *
   * @param modelId
   */
  @Transactional
  public void cancel(Long modelId) {

    ModelTrainMngDto.Basic master = modelTrainMngCoreService.findModelById(modelId);

    Long jobId = master.getCurrentAttemptId();
    if (jobId == null) {
      throw new IllegalStateException("실행중인 작업이 없습니다.");
    }

    var job =
        modelTrainJobCoreService
            .findById(jobId)
            .orElseThrow(() -> new IllegalStateException("Job not found"));

    String containerName = job.getContainerName();

    // 1) 컨테이너 강제 종료 + 제거 (없거나 이미 죽었어도 괜찮게)
    if (containerName != null && !containerName.isBlank()) {
      dockerTrainService.killContainer(containerName);
    }

    // 2) 상태 업데이트 (항상 수행)
    modelTrainJobCoreService.markCanceled(jobId);
    modelTrainMngCoreService.markStopped(modelId);
  }
}
