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

import com.kamco.cd.training.common.dto.HyperParam;
import com.kamco.cd.training.common.enums.HyperParamSelectType;
import com.kamco.cd.training.common.enums.TrainType;
import com.kamco.cd.training.common.exception.CustomApiException;
import com.kamco.cd.training.dataset.dto.DatasetDto.DatasetReq;
import com.kamco.cd.training.dataset.dto.DatasetDto.SelectDataSet;
import com.kamco.cd.training.hyperparam.dto.HyperParamDto;
import com.kamco.cd.training.model.dto.ModelConfigDto;
import com.kamco.cd.training.model.dto.ModelTrainMngDto;
import com.kamco.cd.training.model.dto.ModelTrainMngDto.SearchReq;
import com.kamco.cd.training.postgres.core.HyperParamCoreService;
import com.kamco.cd.training.postgres.core.ModelTrainMngCoreService;
import com.kamco.cd.training.train.service.TrainJobService;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
@Slf4j
public class ModelTrainMngService {

  private final TrainJobService trainJobService;
  private final ModelTrainMngCoreService modelTrainMngCoreService;
  private final HyperParamCoreService hyperParamCoreService;

  /**
   * 모델학습 조회
   *
   * @param searchReq 검색 조건
   * @return 페이징 처리된 모델 목록
   */
  public Page<ModelTrainMngDto.Basic> getModelList(SearchReq searchReq) {
    return modelTrainMngCoreService.findByModelList(searchReq);
  }

  /**
   * 모델학습 삭제
   *
   * @param uuid
   */
  @Transactional
  public void deleteModelTrain(UUID uuid) {
    modelTrainMngCoreService.deleteModel(uuid);
  }

  /**
   * 모델학습 등록
   *
   * @param req
   * @return
   */
  @Transactional
  public void createModelTrain(ModelTrainMngDto.AddReq req) {
    HyperParam hyperParam = req.getHyperParam();
    HyperParamDto.Basic hyper = new HyperParamDto.Basic();

    // 전이 학습은 모델 선택 필수
    if (TrainType.TRANSFER.getId().equals(req.getTrainType())) {
      if (req.getBeforeModelId() == null) {
        throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST, "모델을 선택해 주세요.");
      }
    }

    // 하이파라미터 신규저장
    if (HyperParamSelectType.NEW.getId().equals(req.getHyperParamType())) {
      // 하이퍼파라미터 등록
      hyper = hyperParamCoreService.createHyperParam(hyperParam);
      req.setHyperUuid(hyper.getUuid());
    }

    // 모델학습 테이블 저장
    Long modelId = modelTrainMngCoreService.saveModel(req);

    // 모델학습 데이터셋 저장
    modelTrainMngCoreService.saveModelDataset(modelId, req);

    // 모델 데이터셋 mapping 저장
    modelTrainMngCoreService.saveModelDatasetMap(
        modelId, req.getTrainingDataset().getDatasetList());

    // 모델 config 저장
    modelTrainMngCoreService.saveModelConfig(modelId, req.getModelConfig());

    // 저장 다 끝난 뒤에 job enqueue
    if (Boolean.TRUE.equals(req.getIsStart())) {
      trainJobService.enqueue(modelId); // job 저장 + 이벤트 발행(실행은 AFTER_COMMIT)
    }
  }

  /**
   * 모델학습 모델학습 uuid config정보 조회
   *
   * @param uuid 모델학습 uuid
   * @return
   */
  public ModelConfigDto.Basic getModelConfigByModelId(UUID uuid) {
    return modelTrainMngCoreService.findModelConfigByModelId(uuid);
  }

  /**
   * 모델별 데이터셋 목록 조회
   *
   * @param req
   * @return
   */
  public List<SelectDataSet> getDatasetSelectList(DatasetReq req) {
    if (req.getModelNo().equals("G1")) {
      return modelTrainMngCoreService.getDatasetSelectG1List(req);
    } else {
      return modelTrainMngCoreService.getDatasetSelectG2G3List(req);
    }
  }
}
