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

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.QMapSheetMngFileEntity.mapSheetMngFileEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity;
import static com.kamco.cd.kamcoback.postgres.entity.QYearEntity.yearEntity;

import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
import com.kamco.cd.kamcoback.postgres.entity.QYearEntity;
import com.kamco.cd.kamcoback.postgres.entity.YearEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.CaseBuilder;
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.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.validation.Valid;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.hibernate.query.Query;
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)");

  @PersistenceContext private EntityManager em;

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

  @Override
  public List<MapSheetMngDto.MngDto> findMapSheetMngList() {

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

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

    NumberExpression<Long> totalCount = mapSheetMngHstEntity.count().as("syncTotCnt");

    NumberExpression<Long> doneCount =
        new CaseBuilder()
            .when(mapSheetMngHstEntity.dataState.eq("DONE"))
            .then(1L)
            .otherwise(0L)
            .sum()
            .as("syncStateDoneCnt");

    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.mngYyyy,
                    mapSheetMngEntity.mngPath,
                    mapSheetMngEntity.mngState,
                    mapSheetMngEntity.syncState,
                    mapSheetMngEntity.syncCheckState,
                    mapSheetMngHstEntity.count().as("syncTotalCnt"),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.dataState.eq("DONE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum()
                        .as("syncStateDoneCnt"),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.ne("NOTYET"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("NOTPAIR")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("DUPLICATE")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncState
                                .eq("TYPEERROR")
                                .or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(
                                    mapSheetMngHstEntity
                                        .syncState
                                        .eq("TYPEERROR")
                                        .or(mapSheetMngHstEntity.syncState.eq("SIZEERROR"))))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("NOFILE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    mapSheetMngEntity.createdDttm,
                    mapSheetMngHstEntity.syncEndDttm.max()))
            .from(mapSheetMngEntity)
            .leftJoin(mapSheetMngHstEntity)
            .on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
            .where(whereBuilder)
            // .offset(pageable.getOffset())
            // .limit(pageable.getPageSize())
            .orderBy(mapSheetMngEntity.mngYyyy.desc())
            .groupBy(mapSheetMngEntity.mngYyyy)
            .fetch();

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

    return foundContent;
  }

  public List<Integer> findMapSheetMngYyyyList() {

    List<Integer> foundContent =
        queryFactory
            .select(yearEntity.yyyy)
            .from(yearEntity)
            .leftJoin(mapSheetMngEntity)
            .on(mapSheetMngEntity.mngYyyy.eq(yearEntity.yyyy))
            .where(yearEntity.status.eq("NOTYET"))
            .orderBy(yearEntity.yyyy.asc())
            .limit(10)
            .fetch();

    foundContent.sort(Comparator.reverseOrder());

    return foundContent;
  }

  public void updateYearState(int yyyy, String status) {
    long execCount =
        queryFactory
            .update(yearEntity)
            .set(yearEntity.status, status)
            .where(yearEntity.yyyy.eq(yyyy))
            .execute();
  }

  public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {

    BooleanBuilder whereBuilder = new BooleanBuilder();
    whereBuilder.and(mapSheetMngEntity.mngYyyy.eq(mngYyyy));

    MapSheetMngDto.MngDto foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngDto.class,
                    Expressions.numberTemplate(
                        Integer.class,
                        "row_number() over(order by {0} desc)",
                        mapSheetMngEntity.createdDttm),
                    mapSheetMngEntity.mngYyyy,
                    mapSheetMngEntity.mngPath,
                    mapSheetMngEntity.mngState,
                    mapSheetMngEntity.syncState,
                    mapSheetMngEntity.syncCheckState,
                    mapSheetMngHstEntity.count().as("syncTotalCnt"),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.dataState.eq("DONE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum()
                        .as("syncStateDoneCnt"),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.ne("NOTYET"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("NOTPAIR"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("NOTPAIR")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("DUPLICATE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("DUPLICATE")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncState
                                .eq("TYPEERROR")
                                .or(mapSheetMngHstEntity.syncState.eq("SIZEERROR")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(
                                    mapSheetMngHstEntity
                                        .syncState
                                        .eq("TYPEERROR")
                                        .or(mapSheetMngHstEntity.syncState.eq("SIZEERROR"))))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(mapSheetMngHstEntity.syncState.eq("NOFILE"))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    new CaseBuilder()
                        .when(
                            mapSheetMngHstEntity
                                .syncCheckState
                                .eq("DONE")
                                .and(mapSheetMngHstEntity.syncState.eq("NOFILE")))
                        .then(1L)
                        .otherwise(0L)
                        .sum(),
                    mapSheetMngEntity.createdDttm,
                    mapSheetMngHstEntity.syncEndDttm.max()))
            .from(mapSheetMngEntity)
            .leftJoin(mapSheetMngHstEntity)
            .on(mapSheetMngEntity.mngYyyy.eq(mapSheetMngHstEntity.mngYyyy))
            .where(whereBuilder)
            .groupBy(mapSheetMngEntity.mngYyyy)
            .fetchOne();

    return foundContent;
  }

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

    Pageable pageable = PageRequest.of(searchReq.getPage(), searchReq.getSize());
    BooleanBuilder whereBuilder = new BooleanBuilder();

    whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(searchReq.getMngYyyy()));
    whereBuilder.and(
        mapSheetMngHstEntity.syncState.ne("DONE").and(mapSheetMngHstEntity.syncState.ne("NOTYET")));

    if (searchReq.getSyncState() != null && !searchReq.getSyncState().isEmpty()) {
      whereBuilder.and(mapSheetMngHstEntity.syncState.eq(searchReq.getSyncState()));
    }

    if (searchReq.getSyncCheckState() != null && !searchReq.getSyncCheckState().isEmpty()) {
      whereBuilder.and(mapSheetMngHstEntity.syncCheckState.eq(searchReq.getSyncCheckState()));
    }

    if (searchReq.getSearchValue() != null && !searchReq.getSearchValue().isEmpty()) {
      whereBuilder.and(
          mapSheetMngHstEntity
              .mapSheetNum
              .eq(searchReq.getSearchValue())
              .or(mapSheetMngHstEntity.refMapSheetNum.eq(searchReq.getSearchValue()))
              .or(
                  Expressions.stringTemplate(
                          "concat({0},substring({1}, 0, 6))",
                          mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
                      .likeIgnoreCase("%" + searchReq.getSearchValue() + "%"))
              .or(
                  Expressions.stringTemplate(
                          "concat({0},substring({1}, 6, 8))",
                          mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
                      .likeIgnoreCase("%" + searchReq.getSearchValue() + "%")));
    }

    List<MapSheetMngDto.ErrorDataDto> foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.ErrorDataDto.class,
                    mapSheetMngHstEntity.hstUid,
                    mapSheetMngHstEntity.mngYyyy,
                    mapSheetMngHstEntity.mapSheetNum,
                    mapSheetMngHstEntity.refMapSheetNum,
                    Expressions.stringTemplate(
                            "concat({0},substring({1}, 0, 6))",
                            mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
                        .as("map50kName"),
                    Expressions.stringTemplate(
                            "concat({0},{1})",
                            mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
                        .as("map5kName"),
                    Expressions.stringTemplate(
                            "concat({0},substring({1}, 6, 8))",
                            mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum)
                        .as("mapSrcName"),
                    mapInkx5kEntity.fid,
                    mapSheetMngHstEntity.createdDate,
                    mapSheetMngHstEntity.syncState,
                    mapSheetMngHstEntity.syncTfwFileName,
                    mapSheetMngHstEntity.syncTifFileName,
                    mapSheetMngHstEntity.syncCheckState,
                    mapSheetMngHstEntity.syncCheckTfwFileName,
                    mapSheetMngHstEntity.syncCheckTifFileName))
            .from(mapSheetMngHstEntity)
            .innerJoin(mapInkx5kEntity)
            .on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
            .where(whereBuilder)
            .orderBy(mapSheetMngHstEntity.createdDate.desc())
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .fetch();

    Long countQuery =
        queryFactory
            .select(mapSheetMngHstEntity.hstUid.count())
            .from(mapSheetMngHstEntity)
            .innerJoin(mapInkx5kEntity)
            .on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
            .where(whereBuilder)
            .fetchOne();

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

  @Override
  public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) {

    BooleanBuilder whereBuilder = new BooleanBuilder();
    whereBuilder.and(mapSheetMngHstEntity.hstUid.eq(hstUid));

    MapSheetMngDto.ErrorDataDto foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.ErrorDataDto.class,
                    mapSheetMngHstEntity.hstUid,
                    mapSheetMngHstEntity.mngYyyy,
                    mapSheetMngHstEntity.mapSheetNum,
                    mapSheetMngHstEntity.refMapSheetNum,
                    Expressions.stringTemplate(
                        "concat({0}, substring({1}, 1, 5))",
                        mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum),

                    // ✅ 튜플 방지: concat으로 문자열 생성
                    Expressions.stringTemplate(
                        "concat('(', {0}, ',', {1}, ')')",
                        mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum),
                    Expressions.stringTemplate(
                        "concat({0}, substring({1}, 6, 3))",
                        mapInkx5kEntity.mapidNm, mapSheetMngHstEntity.mapSheetNum),

                    // fid 타입 주의 (Long이면 DTO도 Long으로 맞추는 걸 추천)
                    mapInkx5kEntity.fid, // 또는 mapInkx5kEntity.fid.intValue()

                    // ✅ createdDate 말고 ZonedDateTime으로 매핑된 필드로
                    mapSheetMngHstEntity.createdDate, // (예시)
                    mapSheetMngHstEntity.syncState,
                    mapSheetMngHstEntity.syncTfwFileName,
                    mapSheetMngHstEntity.syncTifFileName,
                    mapSheetMngHstEntity.syncCheckState,
                    mapSheetMngHstEntity.syncCheckTfwFileName,
                    mapSheetMngHstEntity.syncCheckTifFileName))
            .from(mapSheetMngHstEntity)
            .innerJoin(mapInkx5kEntity)
            .on(mapSheetMngHstEntity.mapSheetNum.eq(mapInkx5kEntity.mapidcdNo))
            .where(whereBuilder)
            .fetchOne();

    return foundContent;
  }

  @Override
  public List<MapSheetMngDto.MngFilesDto> findByHstUidMapSheetFileList(Long hstUid) {

    BooleanBuilder whereBuilder = new BooleanBuilder();
    whereBuilder.and(mapSheetMngFileEntity.hstUid.eq(hstUid));
    whereBuilder.and(mapSheetMngFileEntity.fileDel.eq(false));

    List<MapSheetMngDto.MngFilesDto> foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngFilesDto.class,
                    mapSheetMngFileEntity.fileUid,
                    mapSheetMngFileEntity.mngYyyy,
                    mapSheetMngFileEntity.mapSheetNum,
                    mapSheetMngFileEntity.refMapSheetNum,
                    mapSheetMngFileEntity.filePath,
                    mapSheetMngFileEntity.fileName,
                    mapSheetMngFileEntity.fileExt,
                    mapSheetMngFileEntity.hstUid,
                    mapSheetMngFileEntity.fileSize))
            .from(mapSheetMngFileEntity)
            .where(whereBuilder)
            .fetch();

    return foundContent;
  }

  @Override
  public List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid) {
    BooleanBuilder whereBuilder = new BooleanBuilder();
    whereBuilder.and(mapSheetMngFileEntity.hstUid.eq(hstUid));
    whereBuilder.and(mapSheetMngFileEntity.fileDel.eq(false));

    List<MapSheetMngDto.MngFilesDto> foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngFilesDto.class,
                    mapSheetMngFileEntity.fileUid,
                    mapSheetMngFileEntity.mngYyyy,
                    mapSheetMngFileEntity.mapSheetNum,
                    mapSheetMngFileEntity.refMapSheetNum,
                    mapSheetMngFileEntity.filePath,
                    mapSheetMngFileEntity.fileName,
                    mapSheetMngFileEntity.fileExt,
                    mapSheetMngFileEntity.hstUid,
                    mapSheetMngFileEntity.fileSize))
            .from(mapSheetMngFileEntity)
            .where(whereBuilder)
            .orderBy(mapSheetMngFileEntity.filePath.asc())
            .fetch();

    return foundContent;
  }

  @Override
  public MapSheetMngDto.MngFilesDto findYyyyToMapSheetFilePathRefer(int mngYyyy) {

    BooleanBuilder whereBuilder = new BooleanBuilder();
    whereBuilder.and(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy));

    MapSheetMngDto.MngFilesDto foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngFilesDto.class,
                    mapSheetMngFileEntity.fileUid,
                    mapSheetMngFileEntity.mngYyyy,
                    mapSheetMngFileEntity.mapSheetNum,
                    mapSheetMngFileEntity.refMapSheetNum,
                    mapSheetMngFileEntity.filePath,
                    mapSheetMngFileEntity.fileName,
                    mapSheetMngFileEntity.fileExt,
                    mapSheetMngFileEntity.hstUid,
                    mapSheetMngFileEntity.fileSize))
            .from(mapSheetMngFileEntity)
            .where(whereBuilder)
            .orderBy(mapSheetMngFileEntity.fileUid.desc())
            .limit(1)
            .fetchOne();

    return foundContent;
  }

  @Override
  public MapSheetMngDto.MngFilesDto findByFileUidMapSheetFile(Long fileUid) {

    MapSheetMngDto.MngFilesDto foundContent =
        queryFactory
            .select(
                Projections.constructor(
                    MapSheetMngDto.MngFilesDto.class,
                    mapSheetMngFileEntity.fileUid,
                    mapSheetMngFileEntity.mngYyyy,
                    mapSheetMngFileEntity.mapSheetNum,
                    mapSheetMngFileEntity.refMapSheetNum,
                    mapSheetMngFileEntity.filePath,
                    mapSheetMngFileEntity.fileName,
                    mapSheetMngFileEntity.fileExt,
                    mapSheetMngFileEntity.hstUid,
                    mapSheetMngFileEntity.fileSize))
            .from(mapSheetMngFileEntity)
            .where(mapSheetMngFileEntity.fileUid.eq(fileUid))
            .fetchOne();

    return foundContent;
  }

  @Override
  public void MapSheetMngComplete(int mngYyyy) {
    long execCount =
        queryFactory
            .update(mapSheetMngEntity)
            .set(mapSheetMngEntity.mngState, "COMPLETE")
            .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy))
            .execute();
  }

  @Override
  public void updateMapSheetMngHstSyncCheckState(MapSheetMngDto.SyncCheckStateReqUpdateDto updReq) {

    long execCount =
        queryFactory
            .update(mapSheetMngHstEntity)
            .set(mapSheetMngHstEntity.syncCheckState, updReq.getSyncCheckState())
            .set(mapSheetMngHstEntity.mapSheetPath, updReq.getFilePath())
            .set(mapSheetMngHstEntity.syncCheckTfwFileName, updReq.getSyncCheckTfwFileName())
            .set(mapSheetMngHstEntity.syncCheckTifFileName, updReq.getSyncCheckTifFileName())
            .set(mapSheetMngHstEntity.syncCheckStrtDttm, ZonedDateTime.now())
            .set(mapSheetMngHstEntity.syncCheckEndDttm, ZonedDateTime.now())
            .where(mapSheetMngHstEntity.hstUid.eq(updReq.getHstUid()))
            .execute();
  }

  @Override
  public void deleteByMngYyyyMngAll(int mngYyyy) {

    long deletedFileCount =
        queryFactory
            .delete(mapSheetMngFileEntity)
            .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy))
            .execute();

    long deletedHisCount =
        queryFactory
            .delete(mapSheetMngHstEntity)
            .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy))
            .execute();

    long deletedMngCount =
        queryFactory
            .delete(mapSheetMngEntity)
            .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy))
            .execute();
  }

  @Override
  public void deleteByMngYyyyMng(int mngYyyy) {

    long deletedMngCount =
        queryFactory
            .delete(mapSheetMngEntity)
            .where(mapSheetMngEntity.mngYyyy.eq(mngYyyy))
            .execute();
  }

  @Override
  public void deleteByMngYyyyMngHst(int mngYyyy) {

    long deletedHisCount =
        queryFactory
            .delete(mapSheetMngHstEntity)
            .where(mapSheetMngHstEntity.mngYyyy.eq(mngYyyy))
            .execute();
  }

  @Override
  public void deleteByMngYyyyMngFile(int mngYyyy) {

    long deletedFileCount =
        queryFactory
            .delete(mapSheetMngFileEntity)
            .where(mapSheetMngFileEntity.mngYyyy.eq(mngYyyy))
            .execute();
  }

  @Override
  public void deleteByHstUidMngFile(Long hstUid) {
    long deletedFileCount =
        queryFactory
            .update(mapSheetMngFileEntity)
            .set(mapSheetMngFileEntity.fileDel, true)
            .where(mapSheetMngFileEntity.hstUid.eq(hstUid))
            .execute();
  }

  @Override
  public void deleteByFileUidMngFile(Long fileUid) {
    long fileCount =
        queryFactory
            .update(mapSheetMngFileEntity)
            .set(mapSheetMngFileEntity.fileDel, true)
            .where(mapSheetMngFileEntity.fileUid.eq(fileUid))
            .execute();
  }

  @Override
  public void updateByHstUidMngFileState(Long hstUid, String fileState) {
    long execCount =
        queryFactory
            .update(mapSheetMngFileEntity)
            .set(mapSheetMngFileEntity.fileState, fileState)
            .where(mapSheetMngFileEntity.hstUid.eq(hstUid))
            .execute();
  }

  @Override
  public void deleteByNotInFileUidMngFile(Long hstUid, List<Long> fileUids) {
    long execCount =
        queryFactory
            .update(mapSheetMngFileEntity)
            .set(mapSheetMngFileEntity.fileDel, true)
            .where(
                mapSheetMngFileEntity
                    .hstUid
                    .eq(hstUid)
                    .and(mapSheetMngFileEntity.fileUid.notIn(fileUids)))
            .execute();
  }

  @Override
  public void updateByFileUidMngFileState(Long fileUid, String fileState) {
    long execCount =
        queryFactory
            .update(mapSheetMngFileEntity)
            .set(mapSheetMngFileEntity.fileState, fileState)
            .where(mapSheetMngFileEntity.fileUid.eq(fileUid))
            .execute();
  }

  @Override
  public int findByYearFileNameFileCount(int mngYyyy, String fileName) {
    Long execCount =
        queryFactory
            .select(mapSheetMngFileEntity.count())
            .from(mapSheetMngFileEntity)
            .where(
                mapSheetMngFileEntity
                    .mngYyyy
                    .eq(mngYyyy)
                    .and(mapSheetMngFileEntity.fileName.eq(fileName)))
            .fetchOne();

    return Math.toIntExact(execCount);
  }

  @Override
  public void mngFileSave(@Valid MapSheetMngDto.MngFileAddReq addReq) {
    long fileCount =
        queryFactory
            .insert(mapSheetMngFileEntity)
            .columns(
                mapSheetMngFileEntity.mngYyyy,
                mapSheetMngFileEntity.mapSheetNum,
                mapSheetMngFileEntity.refMapSheetNum,
                mapSheetMngFileEntity.filePath,
                mapSheetMngFileEntity.fileName,
                mapSheetMngFileEntity.fileExt,
                mapSheetMngFileEntity.hstUid,
                mapSheetMngFileEntity.fileSize,
                mapSheetMngFileEntity.fileState)
            .values(
                addReq.getMngYyyy(),
                addReq.getMapSheetNum(),
                addReq.getRefMapSheetNum(),
                addReq.getFilePath(),
                addReq.getFileName(),
                addReq.getFileExt(),
                addReq.getHstUid(),
                addReq.getFileSize(),
                addReq.getFileState())
            .execute();
  }

  @Override
  public int insertMapSheetOrgDataToMapSheetMngHst(int mngYyyy, String mngPath) {

    String sql =
        """
            INSERT INTO tb_map_sheet_mng_hst
            (
              mng_yyyy
              ,map_sheet_path
              ,map_sheet_code
              ,map_sheet_num
              ,map_sheet_name
              ,map_sheet_code_src
              ,scale_ratio
              ,ref_map_sheet_num
              ,use_inference
            )
            select
              :mngYyyy as mng_yyyy,
              :mngPath as map_sheet_path,
              fid as map_sheet_code,
              mapidcd_no as map_sheet_num,
              mapid_nm as map_sheet_name,
              fid as map_sheet_code_src,
              5000 as scale_ratio,
              ((mapidcd_no::INTEGER)/1000) as ref_map_sheet_num,
              use_inference
            from
              tb_map_inkx_5k
        """;

    // Native Query 생성 및 실행
    Query query = (Query) em.createNativeQuery(sql);
    query.setParameter("mngYyyy", mngYyyy);
    query.setParameter("mngPath", mngPath);

    int exeCnt = query.executeUpdate(); // 실행 (영향받은 행의 개수 반환)

    return exeCnt;
  }

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

  @Override
  public void updateHstFileSizes(
      Long hstUid, long tifSizeBytes, long tfwSizeBytes, long totalSizeBytes) {
    String sql =
        "UPDATE tb_map_sheet_mng_hst SET tif_size_bytes = :tif, tfw_size_bytes = :tfw, total_size_bytes = :tot WHERE hst_uid = :uid";
    Query query = (Query) em.createNativeQuery(sql);
    query.setParameter("tif", tifSizeBytes);
    query.setParameter("tfw", tfwSizeBytes);
    query.setParameter("tot", totalSizeBytes);
    query.setParameter("uid", hstUid);
    query.executeUpdate();
  }

  @Override
  public Page<YearEntity> getYears(YearSearchReq req) {
    Pageable pageable = req.toPageable();
    // LISTS
    List<YearEntity> content =
        queryFactory
            .selectFrom(yearEntity)
            .where(eqYearStatus(yearEntity, req.getStatus()))
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(yearEntity.yyyy.asc())
            .fetch();
    // count 쿼리
    Long total =
        queryFactory
            .select(yearEntity.yyyy.count())
            .from(yearEntity)
            .where(eqYearStatus(yearEntity, req.getStatus()))
            .fetchOne();
    return new PageImpl<>(content, pageable, total);
  }

  private BooleanExpression eqYearStatus(QYearEntity years, String status) {
    if (status == null) {
      return null;
    }
    return years.status.eq(status);
  }

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

  private BooleanExpression mapSheetErrorSearchValue(MapSheetMngDto.ErrorSearchReq 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));
  }
}
