package com.kamco.cd.kamcoback.postgres.repository.changedetection;

import static com.kamco.cd.kamcoback.postgres.entity.QDemoLearningAnalysisSceneItemEntity.demoLearningAnalysisSceneItemEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QImageryEntity.imageryEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataEntity.mapSheetAnalDataEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataGeomEntity.mapSheetAnalDataGeomEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalSttcEntity.mapSheetAnalSttcEntity;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.CaseBuilder;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
    implements ChangeDetectionRepositoryCustom {

  private final JPAQueryFactory queryFactory;

  public ChangeDetectionRepositoryImpl(JPAQueryFactory queryFactory) {
    super(MapSheetAnalDataGeomEntity.class);
    this.queryFactory = queryFactory;
  }

  @Override
  public String getPolygonToPoint() {

    return null;
  }

  @Override
  public List<String> findPolygonJson() {
    return queryFactory
        .select(Expressions.stringTemplate("ST_AsGeoJSON({0})", mapSheetAnalDataGeomEntity.geom))
        .from(mapSheetAnalDataGeomEntity)
        .orderBy(mapSheetAnalDataGeomEntity.id.desc())
        .fetch();
  }

  @Override
  public List<ChangeDetectionDto.CountDto> getChangeDetectionClassCount(
      Long id, String mapSheetNum) {
    return queryFactory
        .select(
            Projections.constructor(
                ChangeDetectionDto.CountDto.class,
                mapSheetAnalSttcEntity.id.classAfterCd.as("classCd"),
                mapSheetAnalSttcEntity.id.classAfterCd.as("classNm"), // 앞단 CoreService 에서 한글명으로 변환
                mapSheetAnalSttcEntity.classAfterCnt.sum()))
        .from(mapSheetAnalEntity)
        .innerJoin(mapSheetAnalDataEntity)
        .on(mapSheetAnalDataEntity.analUid.eq(mapSheetAnalEntity.id))
        .innerJoin(mapSheetAnalSttcEntity)
        .on(mapSheetAnalSttcEntity.id.dataUid.eq(mapSheetAnalDataEntity.id))
        .where(
            mapSheetAnalEntity.id.eq(id),
            mapSheetAnalSttcEntity.id.mapSheetNum.eq(Long.valueOf(mapSheetNum)))
        .groupBy(mapSheetAnalSttcEntity.id.classAfterCd)
        .fetch();
  }

  @Override
  public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) {
    ChangeDetectionDto.CogUrlData data =
        queryFactory
            .select(
                Projections.constructor(
                    ChangeDetectionDto.CogUrlData.class,
                    makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"),
                    makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"),
                    Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom)
                        .as("bbox")))
            .from(imageryEntity)
            .innerJoin(mapInkx5kEntity)
            .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo))
            .where(
                imageryEntity
                    .year
                    .eq(req.getBeforeYear())
                    .or(imageryEntity.year.eq(req.getAfterYear())),
                imageryEntity.scene5k.eq(req.getMapSheetNum()))
            .groupBy(mapInkx5kEntity.geom)
            .fetchOne();

    ObjectMapper mapper = new ObjectMapper();
    String geoJson = Objects.requireNonNull(data).getBbox();
    JsonNode jsonNode;
    try {
      jsonNode = mapper.readTree(geoJson);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }

    return new ChangeDetectionDto.CogUrlDto(
        data.getBeforeCogUrl(), data.getAfterCogUrl(), jsonNode);
  }

  @Override
  public List<ChangeDetectionDto.AnalYearList> getChangeDetectionYearList() {
    return queryFactory
        .select(
            Projections.constructor(
                ChangeDetectionDto.AnalYearList.class,
                mapSheetAnalEntity.id,
                mapSheetAnalEntity.analTitle,
                mapSheetAnalEntity.compareYyyy.as("beforeYear"),
                mapSheetAnalEntity.targetYyyy.as("afterYear"),
                mapSheetAnalEntity.baseMapSheetNum))
        .from(mapSheetAnalEntity)
        .orderBy(mapSheetAnalEntity.id.asc())
        .fetch();
  }

  @Override
  public ChangeDetectionDto.PolygonFeatureList getChangeDetectionPolygonList(
      Long analUid, String mapSheetNum) {

    List<ChangeDetectionDto.PolygonQueryData> list =
        queryFactory
            .select(
                Projections.constructor(
                    ChangeDetectionDto.PolygonQueryData.class,
                    Expressions.stringTemplate("{0}", "Feature"),
                    Expressions.stringTemplate(
                        "ST_AsGeoJSON({0})", mapSheetAnalDataGeomEntity.geom),
                    mapSheetAnalDataGeomEntity.id,
                    mapSheetAnalDataGeomEntity.area,
                    mapSheetAnalDataGeomEntity.compareYyyy,
                    mapSheetAnalDataGeomEntity.classBeforeProb,
                    mapSheetAnalDataGeomEntity.classBeforeCd.toUpperCase(),
                    mapSheetAnalDataGeomEntity.targetYyyy,
                    mapSheetAnalDataGeomEntity.classAfterProb,
                    mapSheetAnalDataGeomEntity.classAfterCd.toUpperCase(),
                    mapSheetAnalDataGeomEntity.cdProb))
            .from(mapSheetAnalDataGeomEntity)
            .innerJoin(mapSheetAnalDataEntity)
            .on(mapSheetAnalDataGeomEntity.dataUid.eq(mapSheetAnalDataEntity.id))
            .innerJoin(mapSheetAnalEntity)
            .on(mapSheetAnalEntity.id.eq(mapSheetAnalDataEntity.analUid))
            .where(
                mapSheetAnalEntity.id.eq(analUid),
                mapSheetAnalDataGeomEntity.mapSheetNum.eq(Long.valueOf(mapSheetNum)))
            .fetch();

    ObjectMapper mapper = new ObjectMapper();
    List<ChangeDetectionDto.PolygonFeature> result =
        list.stream()
            .map(
                data -> {
                  String geoJson = data.getGeometry();
                  JsonNode jsonNode;
                  try {
                    jsonNode = mapper.readTree(geoJson);
                  } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                  }

                  ChangeDetectionDto.PolygonProperties properties =
                      new ChangeDetectionDto.PolygonProperties(
                          data.getGeoUid(),
                          data.getArea(),
                          data.getBeforeYear(),
                          data.getBeforeConfidence(),
                          data.getBeforeClass(),
                          data.getAfterYear(),
                          data.getAfterConfidence(),
                          data.getAfterClass(),
                          data.getCdProb());

                  return new ChangeDetectionDto.PolygonFeature(
                      data.getType(), jsonNode, properties);
                })
            .collect(Collectors.toList());

    ChangeDetectionDto.PolygonFeatureList polygonList = new ChangeDetectionDto.PolygonFeatureList();
    polygonList.setType("FeatureCollection");
    polygonList.setFeatures(result);
    return polygonList;
  }

  @Override
  public ChangeDetectionDto.PointFeatureList getChangeDetectionPointList(
      Long analUid, String mapSheetNum) {
    List<ChangeDetectionDto.PointQueryData> list =
        queryFactory
            .select(
                Projections.constructor(
                    ChangeDetectionDto.PointQueryData.class,
                    Expressions.stringTemplate("{0}", "Feature"),
                    Expressions.stringTemplate(
                        "ST_AsGeoJSON({0})", mapSheetAnalDataGeomEntity.geomCenter), // point
                    Projections.constructor(
                        ChangeDetectionDto.PointProperties.class,
                        mapSheetAnalDataGeomEntity.id,
                        mapSheetAnalDataGeomEntity.classAfterCd.toUpperCase())))
            .from(mapSheetAnalDataGeomEntity)
            .innerJoin(mapSheetAnalDataEntity)
            .on(mapSheetAnalDataGeomEntity.dataUid.eq(mapSheetAnalDataEntity.id))
            .innerJoin(mapSheetAnalEntity)
            .on(mapSheetAnalEntity.id.eq(mapSheetAnalDataEntity.analUid))
            .where(
                mapSheetAnalEntity.id.eq(analUid),
                mapSheetAnalDataGeomEntity.mapSheetNum.eq(Long.valueOf(mapSheetNum)))
            .fetch();

    ObjectMapper mapper = new ObjectMapper();
    List<ChangeDetectionDto.PointFeature> result =
        list.stream()
            .map(
                data -> {
                  String geoJson = data.getGeometry();
                  JsonNode jsonNode;
                  try {
                    jsonNode = mapper.readTree(geoJson);
                  } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                  }

                  return new ChangeDetectionDto.PointFeature(
                      data.getType(), jsonNode, data.getProperties());
                })
            .collect(Collectors.toList());

    return new ChangeDetectionDto.PointFeatureList("FeatureCollection", result);
  }

  @Override
  public List<ChangeDetectionDto.MapSheetList> getChangeDetectionMapSheetList(Long analUid) {
    return queryFactory
        .select(
            Projections.constructor(
                ChangeDetectionDto.MapSheetList.class,
                mapInkx5kEntity.mapidcdNo,
                mapInkx5kEntity.mapidNm,
                mapInkx5kEntity.mapidNm // alias 앞단 core에서
                ))
        .from(demoLearningAnalysisSceneItemEntity)
        .innerJoin(mapInkx5kEntity)
        .on(demoLearningAnalysisSceneItemEntity.fid.eq(mapInkx5kEntity.fid.longValue()))
        .where(demoLearningAnalysisSceneItemEntity.groupId.eq(analUid))
        .orderBy(mapInkx5kEntity.mapidNm.asc(), mapInkx5kEntity.mapidcdNo.asc())
        .fetch();
  }

  private StringExpression makeCogUrl(Integer year) {
    return new CaseBuilder()
        .when(imageryEntity.year.eq(year))
        .then(
            Expressions.stringTemplate(
                "{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
        .otherwise("");
  }
}
