package com.kamco.cd.training.postgres.repository.model;

import static com.kamco.cd.training.postgres.entity.QDatasetEntity.datasetEntity;
import static com.kamco.cd.training.postgres.entity.QDatasetObjEntity.datasetObjEntity;
import static com.kamco.cd.training.postgres.entity.QModelDatasetMappEntity.modelDatasetMappEntity;
import static com.kamco.cd.training.postgres.entity.QModelMasterEntity.modelMasterEntity;

import com.kamco.cd.training.common.enums.DetectionClassification;
import com.kamco.cd.training.common.enums.ModelType;
import com.kamco.cd.training.postgres.entity.ModelDatasetMappEntity;
import com.kamco.cd.training.postgres.entity.QDatasetObjEntity;
import com.kamco.cd.training.postgres.entity.QDatasetTestObjEntity;
import com.kamco.cd.training.postgres.entity.QDatasetValObjEntity;
import com.kamco.cd.training.train.dto.ModelTrainLinkDto;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ModelDatasetMappRepositoryImpl implements ModelDatasetMappRepositoryCustom {

  private final JPAQueryFactory queryFactory;

  @Override
  public List<ModelDatasetMappEntity> findByModelUid(Long modelId) {
    return queryFactory
        .select(modelDatasetMappEntity)
        .from(modelDatasetMappEntity)
        .where(modelDatasetMappEntity.modelUid.eq(modelId))
        .fetch();
  }

  @Override
  public List<ModelTrainLinkDto> findDatasetTrainPath(Long modelId) {
    QDatasetObjEntity datasetObjEntity = QDatasetObjEntity.datasetObjEntity;

    // =====================
    // 조건 분리
    // =====================
    BooleanExpression g1 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G1.getId())
            .and(
                datasetObjEntity.targetClassCd.in(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId()));

    BooleanExpression g2 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G2.getId())
            .and(datasetObjEntity.targetClassCd.eq(DetectionClassification.WASTE.getId()));

    BooleanExpression g4 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G4.getId())
            .and(datasetObjEntity.targetClassCd.eq(DetectionClassification.SOLAR.getId()));

    // G3 = 전체 허용 (fallback)
    BooleanExpression g3 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G3.getId())
            .and(
                datasetObjEntity.targetClassCd.notIn(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId(),
                    DetectionClassification.WASTE.getId(),
                    DetectionClassification.SOLAR.getId()));

    return queryFactory
        .select(
            Projections.constructor(
                ModelTrainLinkDto.class,
                modelMasterEntity.id,
                modelMasterEntity.trainType,
                modelMasterEntity.modelNo,
                modelDatasetMappEntity.datasetUid,
                datasetObjEntity.targetClassCd,
                datasetObjEntity.comparePath,
                datasetObjEntity.targetPath,
                datasetObjEntity.labelPath,
                datasetObjEntity.geojsonPath,
                datasetEntity.uid))
        .from(modelMasterEntity)
        .leftJoin(modelDatasetMappEntity)
        .on(modelDatasetMappEntity.modelUid.eq(modelMasterEntity.id))
        .leftJoin(datasetEntity)
        .on(datasetEntity.id.eq(modelDatasetMappEntity.datasetUid))
        .leftJoin(datasetObjEntity)
        .on(
            datasetObjEntity
                .datasetUid
                .eq(modelDatasetMappEntity.datasetUid)
                .and(g1.or(g2).or(g4).or(g3)))
        .where(modelMasterEntity.id.eq(modelId))
        .fetch();
  }

  @Override
  public List<ModelTrainLinkDto> findDatasetValPath(Long modelId) {

    QDatasetValObjEntity datasetValObjEntity = QDatasetValObjEntity.datasetValObjEntity;

    // =====================
    // 조건 분리
    // =====================
    BooleanExpression g1 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G1.getId())
            .and(
                datasetValObjEntity.targetClassCd.in(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId()));

    BooleanExpression g2 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G2.getId())
            .and(datasetValObjEntity.targetClassCd.eq(DetectionClassification.WASTE.getId()));

    BooleanExpression g4 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G4.getId())
            .and(datasetValObjEntity.targetClassCd.eq(DetectionClassification.SOLAR.getId()));

    // G3 = 전체 허용 (fallback)
    BooleanExpression g3 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G3.getId())
            .and(
                datasetValObjEntity.targetClassCd.notIn(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId(),
                    DetectionClassification.WASTE.getId(),
                    DetectionClassification.SOLAR.getId()));

    return queryFactory
        .select(
            Projections.constructor(
                ModelTrainLinkDto.class,
                modelMasterEntity.id,
                modelMasterEntity.trainType,
                modelMasterEntity.modelNo,
                modelDatasetMappEntity.datasetUid,
                datasetValObjEntity.targetClassCd,
                datasetValObjEntity.comparePath,
                datasetValObjEntity.targetPath,
                datasetValObjEntity.labelPath,
                datasetValObjEntity.geojsonPath,
                datasetEntity.uid))
        .from(modelMasterEntity)
        .leftJoin(modelDatasetMappEntity)
        .on(modelDatasetMappEntity.modelUid.eq(modelMasterEntity.id))
        .leftJoin(datasetEntity)
        .on(datasetEntity.id.eq(modelDatasetMappEntity.datasetUid))
        .leftJoin(datasetValObjEntity)
        .on(
            datasetValObjEntity
                .datasetUid
                .eq(modelDatasetMappEntity.datasetUid)
                .and(g1.or(g2).or(g4).or(g3)))
        .where(modelMasterEntity.id.eq(modelId))
        .fetch();
  }

  @Override
  public List<ModelTrainLinkDto> findDatasetTestPath(Long modelId) {

    QDatasetTestObjEntity datasetTestObjEntity = QDatasetTestObjEntity.datasetTestObjEntity;

    // =====================
    // 조건 분리
    // =====================
    BooleanExpression g1 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G1.getId())
            .and(
                datasetTestObjEntity.targetClassCd.in(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId()));

    BooleanExpression g2 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G2.getId())
            .and(datasetTestObjEntity.targetClassCd.eq(DetectionClassification.WASTE.getId()));

    BooleanExpression g4 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G4.getId())
            .and(datasetTestObjEntity.targetClassCd.eq(DetectionClassification.SOLAR.getId()));

    // G3 = 전체 허용 (fallback)
    BooleanExpression g3 =
        modelMasterEntity
            .modelNo
            .eq(ModelType.G3.getId())
            .and(
                datasetTestObjEntity.targetClassCd.notIn(
                    DetectionClassification.CONTAINER.getId(),
                    DetectionClassification.BUILDING.getId(),
                    DetectionClassification.WASTE.getId(),
                    DetectionClassification.SOLAR.getId()));

    return queryFactory
        .select(
            Projections.constructor(
                ModelTrainLinkDto.class,
                modelMasterEntity.id,
                modelMasterEntity.trainType,
                modelMasterEntity.modelNo,
                modelDatasetMappEntity.datasetUid,
                datasetTestObjEntity.targetClassCd,
                datasetTestObjEntity.comparePath,
                datasetTestObjEntity.targetPath,
                datasetTestObjEntity.labelPath,
                datasetTestObjEntity.geojsonPath,
                datasetEntity.uid))
        .from(modelMasterEntity)
        .leftJoin(modelDatasetMappEntity)
        .on(modelDatasetMappEntity.modelUid.eq(modelMasterEntity.id))
        .leftJoin(datasetEntity)
        .on(datasetEntity.id.eq(modelDatasetMappEntity.datasetUid))
        .leftJoin(datasetTestObjEntity)
        .on(
            datasetTestObjEntity
                .datasetUid
                .eq(modelDatasetMappEntity.datasetUid)
                .and(g1.or(g2).or(g4).or(g3)))
        .where(modelMasterEntity.id.eq(modelId))
        .fetch();
  }
}
