package com.kamco.cd.kamcoback.postgres.core;

import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngEntity;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity;
import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngRepository;
import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MapSheetMngCoreService {

  private final MapSheetMngRepository mapSheetMngRepository;

  private static final String ORIGINAL_IMAGES_PATH = "/app/original-images";

  @Value("{spring.profiles.active}")
  private String activeEnv;

  public List<MapSheetMngDto.MngDto> findMapSheetMngList() {
    return mapSheetMngRepository.findMapSheetMngList();
  }

  public List<Integer> findMapSheetMngYyyyList() {
    return mapSheetMngRepository.findMapSheetMngYyyyList();
  }


  public MapSheetMngDto.MngDto findMapSheetMng(int mngYyyy) {
    return mapSheetMngRepository.findMapSheetMng(mngYyyy);
  }

  public void MapSheetMngComplete(@Valid int mngYyyy) {
    mapSheetMngRepository.MapSheetMngComplete(mngYyyy);
  }

  public Page<MapSheetMngDto.ErrorDataDto> findMapSheetErrorList(
      MapSheetMngDto.@Valid ErrorSearchReq searchReq) {
    return mapSheetMngRepository.findMapSheetErrorList(searchReq);
  }

  public MapSheetMngDto.ErrorDataDto findMapSheetError(Long hstUid) {
    return mapSheetMngRepository.findMapSheetError(hstUid);
  }

  public List<MapSheetMngDto.MngFilesDto> findIdToMapSheetFileList(Long hstUid) {
    return mapSheetMngRepository.findIdToMapSheetFileList(hstUid);
  }

  public MapSheetMngDto.DmlReturn uploadProcess(@Valid List<Long> hstUidList) {
    int count = 0;
    if (!Objects.isNull(hstUidList) && !hstUidList.isEmpty()) {
      for (Long hstUid : hstUidList) {
        Optional<MapSheetMngHstEntity> entity =
            Optional.ofNullable(
                mapSheetMngRepository
                    .findMapSheetMngHstInfo(hstUid)
                    .orElseThrow(EntityNotFoundException::new));

        String localPath = "";
        String rootDir = ORIGINAL_IMAGES_PATH + "/" + entity.get().getMngYyyy();
        if (activeEnv.equals("local")) {
          rootDir = localPath + rootDir;
        }

        String filename = entity.get().getMapSheetNum();
        String[] extensions = {"tif", "tfw"};
        boolean flag = allExtensionsExist(rootDir, filename, extensions);
        if (flag) {
          count += 1;
        }

        // 파일 크기 계산 및 저장
        try (Stream<Path> paths = Files.walk(Paths.get(rootDir))) {
          List<Path> matched =
              paths
                  .filter(Files::isRegularFile)
                  .filter(
                      p -> {
                        String name = p.getFileName().toString();
                        return name.equals(filename + ".tif") || name.equals(filename + ".tfw");
                      })
                  .collect(Collectors.toList());

          long tifSize =
              matched.stream()
                  .filter(p -> p.getFileName().toString().endsWith(".tif"))
                  .mapToLong(
                      p -> {
                        try {
                          return Files.size(p);
                        } catch (IOException e) {
                          return 0L;
                        }
                      })
                  .sum();

          long tfwSize =
              matched.stream()
                  .filter(p -> p.getFileName().toString().endsWith(".tfw"))
                  .mapToLong(
                      p -> {
                        try {
                          return Files.size(p);
                        } catch (IOException e) {
                          return 0L;
                        }
                      })
                  .sum();

          entity.get().setTifSizeBytes(tifSize);
          entity.get().setTfwSizeBytes(tfwSize);
          entity.get().setTotalSizeBytes(tifSize + tfwSize);

          // 엔터티 저장 -> 커스텀 업데이트로 변경
          mapSheetMngRepository.updateHstFileSizes(
              entity.get().getHstUid(), tifSize, tfwSize, tifSize + tfwSize);
        } catch (IOException e) {
          // 크기 계산 실패 시 0으로 저장
          entity.get().setTifSizeBytes(0L);
          entity.get().setTfwSizeBytes(0L);
          entity.get().setTotalSizeBytes(0L);
          mapSheetMngRepository.updateHstFileSizes(entity.get().getHstUid(), 0L, 0L, 0L);
        }

        /*
        MapSheetMngDto.DataState dataState =
            flag ? MapSheetMngDto.DataState.SUCCESS : MapSheetMngDto.DataState.FAIL;
        entity.get().updateDataState(dataState);
        */
      }
    }
    return new MapSheetMngDto.DmlReturn("success", count + "개 업로드 성공하였습니다.");
  }

  public MapSheetMngDto.DmlReturn updateExceptUseInference(@Valid List<Long> hstUidList) {
    if (!Objects.isNull(hstUidList) && !hstUidList.isEmpty()) {
      for (Long hstUid : hstUidList) {
        Optional<MapSheetMngHstEntity> entity =
            Optional.ofNullable(
                mapSheetMngRepository
                    .findMapSheetMngHstInfo(hstUid)
                    .orElseThrow(EntityNotFoundException::new));

        // entity.get().updateUseInference(true);
      }
    }
    return new MapSheetMngDto.DmlReturn("success", hstUidList.size() + "개 추론제외 업데이트 하였습니다.");
  }

  /**
   * 특정 파일명 + 여러 확장자가 모두 존재하는지 확인
   *
   * @param rootDir 검색할 최상위 디렉토리
   * @param filename 파일명 (확장자 제외)
   * @param extensions 확인할 확장자 배열 (예: {"tif", "tfw"})
   * @return 모든 확장자가 존재하면 true, 하나라도 없으면 false
   */
  public static boolean allExtensionsExist(String rootDir, String filename, String... extensions) {
    try (Stream<Path> paths = Files.walk(Paths.get(rootDir))) {

      // 모든 파일명을 Set으로 저장
      Set<String> fileNames =
          paths
              .filter(Files::isRegularFile)
              .map(p -> p.getFileName().toString())
              .collect(Collectors.toSet());

      // 모든 확장자 파일 존재 여부 확인
      for (String ext : extensions) {
        String target = filename + "." + ext;
        if (!fileNames.contains(target)) {
          return false; // 하나라도 없으면 false
        }
      }

      return true; // 모두 존재하면 true

    } catch (IOException e) {
      throw new RuntimeException("File search error", e);
    }
  }

  public MapSheetMngDto.DmlReturn mngDataSave(@Valid MapSheetMngDto.AddReq addReq) {

    MapSheetMngEntity entity = new MapSheetMngEntity();
    entity.setMngYyyy(addReq.getMngYyyy());
    entity.setMngPath(addReq.getMngPath());

    mapSheetMngRepository.deleteByMngYyyyMngAll(addReq.getMngYyyy());

    MapSheetMngEntity saved = mapSheetMngRepository.save(entity);
    int hstCnt = mapSheetMngRepository.insertMapSheetOrgDataToMapSheetMngHst(saved.getMngYyyy());

    return new MapSheetMngDto.DmlReturn("success", saved.getMngYyyy().toString());
  }

  public List<MapSheetMngDto.MngFilesDto> findHstUidToMapSheetFileList(Long hstUid) {
    return mapSheetMngRepository.findHstUidToMapSheetFileList(hstUid);
  }

  public MapSheetMngDto.DmlReturn deleteByFileUidMngFile(Long fileUid) {
    mapSheetMngRepository.deleteByFileUidMngFile(fileUid);

    return new MapSheetMngDto.DmlReturn("success", fileUid + " : 삭제되었습니다.");
  }
}
