package com.kamco.cd.kamcoback.scheduler.service;

import static java.lang.String.CASE_INSENSITIVE_ORDER;

import com.kamco.cd.kamcoback.common.utils.FIleChecker;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngFileJobCoreService;
import com.kamco.cd.kamcoback.scheduler.dto.FileDto;
import com.kamco.cd.kamcoback.scheduler.dto.FileDto.SrchFilesDepthDto;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.scheduler.dto.MapSheetMngDto.MngHstDto;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MapSheetMngFileJobService {

  private final MapSheetMngFileJobCoreService mapSheetMngFileJobCoreService;

  @Transactional
  public void checkMapSheetFileProcess(long targetNum, int mngSyncPageSize) {

    List<MngHstDto> mapSheetFileNotYetList = findTargetMapSheetFileList(targetNum, mngSyncPageSize);

    Long hstUid = 0L;
    String syncState = "";
    String syncCheckState = "";
    String fileState = "";
    String dataState = "";

    SrchFilesDepthDto srchDto = new SrchFilesDepthDto();
    List<FIleChecker.Basic> basicList = new ArrayList<>();

    for (MngHstDto item : mapSheetFileNotYetList) {

      // 도엽별 파일 체크 진행중으로 변경
      item.setDataState("PROCESSING");
      mngHstDataSyncStateUpdate(item);

      // 1. MngHstDto 객체의 필드 값에 접근
      // hstUid = item.getHstUid();
      // syncState = item.getSyncState();

      srchDto.setMaxDepth(10);
      srchDto.setDirPath(item.getSyncMngPath());
      srchDto.setExtension("tif,tfw");
      srchDto.setFileNm(item.getMapSheetNum());
      // srchDto.setFileNm("34602047");

      System.out.println(
          "UID: "
              + hstUid
              + ", 상태: "
              + syncState
              + ", 관리경로: "
              + item.getSyncMngPath()
              + ", 파일명 "
              + item.getMapSheetNum()
              + " .tif,tfw");

      // 도엽번호로 파일 찾기
      // basicList = this.getFilesDepthAll(srchDto);

      basicList =
          FIleChecker.getFilesFromAllDepth(
              srchDto.getDirPath(),
              srchDto.getFileNm(),
              srchDto.getExtension(),
              srchDto.getMaxDepth(),
              srchDto.getSortType(),
              0,
              100);

      int tfwCnt =
          (int)
              basicList.stream().filter(dto -> dto.getExtension().toString().equals("tfw")).count();

      int tifCnt =
          (int)
              basicList.stream().filter(dto -> dto.getExtension().toString().equals("tif")).count();

      syncState = "";
      syncCheckState = "";

      if (tfwCnt == 0 && tifCnt == 0) syncState = "NOFILE";

      for (FIleChecker.Basic item2 : basicList) {
        System.out.println("path: " + item2.getParentPath());
        System.out.println("path: " + item2.getFileNm());
        System.out.println("path: " + item2.getFullPath());

        MapSheetMngDto.MngFileAddReq addReq = new MapSheetMngDto.MngFileAddReq();
        addReq.setMngYyyy(item.getMngYyyy());
        addReq.setMapSheetNum(item.getMapSheetNum());
        addReq.setRefMapSheetNum(item.getRefMapSheetNum());
        addReq.setFilePath(item2.getParentPath());
        addReq.setFileName(item2.getFileNm());
        addReq.setFileExt(item2.getExtension());
        addReq.setFileSize(item2.getFileSize());
        addReq.setHstUid(item.getHstUid());

        fileState = "DONE";
        if (item2.getExtension().equals("tfw")) {
          if (tifCnt == 0) {
            fileState = "NOTPAIR";
            syncState = fileState;
          } else if (tfwCnt > 1) {
            fileState = "DUPLICATE";
            syncState = fileState;
          } else if (item2.getFileSize() == 0) {
            fileState = "SIZEERROR";
            syncState = fileState;
          } else if (!FIleChecker.checkTfw(item2.getFullPath())) {
            fileState = "TYPEERROR";
            syncState = fileState;
          }
        } else if (item2.getExtension().equals("tif")) {
          if (tfwCnt == 0) {
            fileState = "NOTPAIR";
            syncState = fileState;
          } else if (tifCnt > 1) {
            fileState = "DUPLICATE";
            syncState = fileState;
          } else if (item2.getFileSize() == 0) {
            fileState = "SIZEERROR";
            syncState = fileState;
          } else if (!FIleChecker.cmmndGdalInfo(item2.getFullPath())) {
            fileState = "TYPEERROR";
            syncState = fileState;
          }
        }

        addReq.setFileState(fileState);
        MapSheetMngDto.DmlReturn DmlReturn = mngDataSave(addReq);
      }

      // 도엽별 파일 체크 완료로 변경
      item.setDataState("DONE");
      if (syncState.isEmpty()) syncState = "DONE";
      item.setSyncState(syncState);
      mngHstDataSyncStateUpdate(item);

      // srchDto.

      // 2. 출력
      // System.out.println("UID: " + hstUid + ", 상태: " + syncState + ", 관리경로: " +
      // item.getSyncMngPath());

      // 3. (필요하다면) 다른 로직 수행
      // ...
    }
  }

  public int checkIsNoFile(List<FileDto.Basic> basicList) {
    if (basicList == null || basicList.size() == 0) {
      return 0;
    }

    return basicList.size();
  }

  public List<MngHstDto> findTargetMapSheetFileList(long targetNum, int pageSize) {
    return mapSheetMngFileJobCoreService.findTargetMapSheetFileList(targetNum, pageSize);
  }

  public MapSheetMngDto.DmlReturn mngHstDataSyncStateUpdate(MapSheetMngDto.MngHstDto UpdateReq) {
    return mapSheetMngFileJobCoreService.mngHstDataSyncStateUpdate(UpdateReq);
  }

  public MapSheetMngDto.DmlReturn mngDataSave(MapSheetMngDto.MngFileAddReq AddReq) {
    return mapSheetMngFileJobCoreService.mngFileSave(AddReq);
  }

  public List<FileDto.Basic> getFilesDepthAll(SrchFilesDepthDto srchDto) {

    Path startPath = Paths.get(srchDto.getDirPath());
    int maxDepth = srchDto.getMaxDepth();
    String dirPath = srchDto.getDirPath();
    String targetFileNm = srchDto.getFileNm();
    String extension = srchDto.getExtension();
    String sortType = srchDto.getSortType();

    int startPos = srchDto.getStartPos();
    int endPos = srchDto.getEndPos();
    int limit = endPos - startPos + 1;

    Set<String> targetExtensions = createExtensionSet(extension);

    List<FileDto.Basic> fileDtoList = new ArrayList<>();
    SimpleDateFormat dttmFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    int fileTotCnt = 0;
    long fileTotSize = 0;

    try (Stream<Path> stream = Files.walk(startPath, maxDepth)) {

      fileDtoList =
          stream
              .filter(Files::isRegularFile)
              .filter(
                  p ->
                      extension == null
                          || extension.equals("")
                          || extension.equals("*")
                          || targetExtensions.contains(extractExtension(p)))
              .sorted(getFileComparator(sortType))
              .filter(p -> p.getFileName().toString().contains(targetFileNm))
              .skip(startPos)
              .limit(limit)
              .map(
                  path -> {
                    int depth = path.getNameCount();

                    String fileNm = path.getFileName().toString();
                    String ext = FilenameUtils.getExtension(fileNm);
                    String parentFolderNm = path.getParent().getFileName().toString();
                    String parentPath = path.getParent().toString();
                    String fullPath = path.toAbsolutePath().toString();

                    File file = new File(fullPath);
                    long fileSize = file.length();
                    String lastModified = dttmFormat.format(new Date(file.lastModified()));

                    return new FileDto.Basic(
                        fileNm, parentFolderNm, parentPath, fullPath, ext, fileSize, lastModified);
                  })
              .collect(Collectors.toList());

      // fileTotCnt = fileDtoList.size();
      // fileTotSize = fileDtoList.stream().mapToLong(FileDto.Basic::getFileSize).sum();

    } catch (IOException e) {
      System.err.println("파일 I/O 오류 발생: " + e.getMessage());
    }

    return fileDtoList;
  }

  public Set<String> createExtensionSet(String extensionString) {
    if (extensionString == null || extensionString.isBlank()) {
      return Set.of();
    }

    // "java, class" -> ["java", " class"] -> [".java", ".class"]
    return Arrays.stream(extensionString.split(","))
        .map(ext -> ext.trim())
        .filter(ext -> !ext.isEmpty())
        .map(ext -> "." + ext.toLowerCase())
        .collect(Collectors.toSet());
  }

  public String extractExtension(Path path) {
    String filename = path.getFileName().toString();
    int lastDotIndex = filename.lastIndexOf('.');

    // 확장자가 없거나 파일명이 .으로 끝나는 경우
    if (lastDotIndex == -1 || lastDotIndex == filename.length() - 1) {
      return ""; // 빈 문자열 반환
    }

    // 확장자 추출 및 소문자 변환
    return filename.substring(lastDotIndex).toLowerCase();
  }

  public Comparator<Path> getFileComparator(String sortType) {

    // 파일 이름 비교 기본 Comparator (대소문자 무시)
    Comparator<Path> nameComparator =
        Comparator.comparing(path -> path.getFileName().toString(), CASE_INSENSITIVE_ORDER);

    Comparator<Path> dateComparator =
        Comparator.comparing(
            path -> {
              try {
                return Files.getLastModifiedTime(path);
              } catch (IOException e) {
                return FileTime.fromMillis(0);
              }
            });

    if ("name desc".equalsIgnoreCase(sortType)) {
      return nameComparator.reversed();
    } else if ("date".equalsIgnoreCase(sortType)) {
      return dateComparator;
    } else if ("date desc".equalsIgnoreCase(sortType)) {
      return dateComparator.reversed();
    } else {
      return nameComparator;
    }
  }
}
