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

import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx50kEntity.mapInkx50kEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngEntity.mapSheetMngEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity;

import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;

public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport
    implements MapSheetMngRepositoryCustom {

  private final JPAQueryFactory queryFactory;
  private final StringExpression NULL_STRING = Expressions.stringTemplate("cast(null as text)");

  public MapSheetMngRepositoryImpl(JPAQueryFactory queryFactory) {
    super(MapSheetMngHstEntity.class);
    this.queryFactory = queryFactory;
  }

  @Override
  public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
      MapSheetMngDto.@Valid searchReq searchReq) {

    Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
    List<MapSheetMngDto.ErrorDataDto> foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.ErrorDataDto.class,
                    mapSheetMngHstEntity.hstUid,
                    rowNum(),
                    Expressions.stringTemplate(
                        "concat({0}, {1})",
                        mapSheetMngHstEntity.mapSheetName, mapInkx50kEntity.mapidcdNo),
                    Expressions.stringTemplate(
                        "concat({0}, substring({1}, {2}, {3}))",
                        mapSheetMngHstEntity.mapSheetName, mapSheetMngHstEntity.mapSheetNum, 6, 8),
                    mapSheetMngHstEntity.mapSheetCodeSrc,
                    Expressions.stringTemplate(
                        "to_char({0}, 'YYYY-MM-DD')", mapSheetMngHstEntity.createdDate),
                    mapSheetMngHstEntity.dataState))
            .from(mapSheetMngHstEntity)
            .innerJoin(mapInkx5kEntity)
            .on(mapSheetMngHstEntity.mapSheetCode.eq(mapInkx5kEntity.fid))
            .leftJoin(mapInkx50kEntity)
            .on(mapInkx5kEntity.fidK50.eq(mapInkx50kEntity.fid.longValue()))
            .where(
                mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()),
                mapSheetMngHstEntity.dataState.eq(MapSheetMngDto.DataState.FAIL), // 오류만 검색
                mapSheetErrorSearchValue(searchReq))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(mapSheetMngHstEntity.createdDate.desc())
            .fetch();

    Long countQuery =
        queryFactory
            .select(mapSheetMngHstEntity.hstUid.count())
            .from(mapSheetMngHstEntity)
            .innerJoin(mapInkx5kEntity)
            .on(mapSheetMngHstEntity.mapSheetCode.eq(mapInkx5kEntity.fid))
            .leftJoin(mapInkx50kEntity)
            .on(mapInkx5kEntity.fidK50.eq(mapInkx50kEntity.fid.longValue()))
            .where(
                mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()),
                mapSheetMngHstEntity.dataState.eq(MapSheetMngDto.DataState.FAIL), // 오류만 검색
                mapSheetErrorSearchValue(searchReq))
            .fetchOne();

    return new PageImpl<>(foundContent, pageable, countQuery);
  }

  @Override
  public Page<MapSheetMngDto.MngDto> findMapSheetMngList(
      MapSheetMngDto.@Valid searchReq searchReq) {

    Pageable pageable = searchReq.toPageable();
    BooleanBuilder whereBuilder = new BooleanBuilder();

    if (searchReq.getMngYyyy() != null) {
      whereBuilder.and(mapSheetMngEntity.id.eq(searchReq.getMngYyyy()));
    }

    List<MapSheetMngDto.MngDto> foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngDto.class,
                    Expressions.numberTemplate(
                        Integer.class,
                        "row_number() over(order by {0} desc)",
                        mapSheetMngEntity.createdDttm),
                    mapSheetMngEntity.id,
                    mapSheetMngEntity.mngState,
                    mapSheetMngEntity.syncState,
                    Expressions.stringTemplate(
                        "to_char({0}, 'YYYY-MM-DD HH24:MI:SS')", mapSheetMngEntity.mngStateDttm),
                    Expressions.stringTemplate(
                        "to_char({0}, 'YYYY-MM-DD HH24:MI:SS')", mapSheetMngEntity.syncStateDttm),
                    mapSheetMngEntity.mngPath,
                    Expressions.stringTemplate(
                        "to_char({0}, 'YYYY-MM-DD HH24:MI:SS')", mapSheetMngEntity.createdDttm),
                    mapSheetMngEntity.createdUid,
                    Expressions.stringTemplate(
                        "to_char({0}, 'YYYY-MM-DD HH24:MI:SS')", mapSheetMngEntity.updatedDttm),
                    mapSheetMngEntity.updatedUid))
            .from(mapSheetMngEntity)
            .where(whereBuilder)
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(mapSheetMngEntity.createdDttm.desc())
            .fetch();

    Long countQuery =
        queryFactory
            .select(mapSheetMngEntity.id.count())
            .from(mapSheetMngEntity)
            .where(whereBuilder)
            .fetchOne();

    return new PageImpl<>(foundContent, pageable, countQuery);
  }

  @Override
  public Optional<MapSheetMngHstEntity> findMapSheetMngHstInfo(Long hstUid) {
    return Optional.ofNullable(
        queryFactory
            .selectFrom(mapSheetMngHstEntity)
            .where(mapSheetMngHstEntity.hstUid.eq(hstUid))
            .fetchOne());
  }

  private NumberExpression<Integer> rowNum() {
    return Expressions.numberTemplate(
        Integer.class, "row_number() over(order by {0} desc)", mapSheetMngHstEntity.createdDate);
  }

  private BooleanExpression mapSheetErrorSearchValue(MapSheetMngDto.searchReq searchReq) {
    if (Objects.isNull(searchReq.getSearchValue())) {
      return null;
    }

    // 검색어 1개 값이 도엽명 or 도엽번호 like 검색
    return Expressions.booleanTemplate(
            "{0} like '%" + searchReq.getSearchValue() + "%'", mapSheetMngHstEntity.mapSheetName)
        .or(
            Expressions.booleanTemplate(
                "{0} like '%" + searchReq.getSearchValue() + "%'",
                mapSheetMngHstEntity.mapSheetNum));
  }
}
