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

import static java.lang.String.CASE_INSENSITIVE_ORDER;

import com.kamco.cd.kamcoback.mapsheet.dto.FileDto;
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FilesDto;
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFilesDepthDto;
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.SrchFilesDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.DmlReturn;
import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngFilesDto;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService;
import jakarta.validation.Valid;
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.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

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

  private final MapSheetMngCoreService mapSheetMngCoreService;

  public FilesDto getFilesAll(SrchFilesDto srchDto) {

    String dirPath = srchDto.getDirPath();
    String extension = srchDto.getExtension();
    String sortType = srchDto.getSortType();
    int startPos = srchDto.getStartPos();
    int endPos = srchDto.getEndPos();
    File dir = new File(dirPath);
    File[] fileList = dir.listFiles();

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

    int fileListPos = 0;
    int fileTotCnt = 0;
    long fileTotSize = 0;

    if (fileList != null) {
      if (sortType.equals("name")) {
        Arrays.sort(fileList);
      } else if (sortType.equals("date")) {
        Arrays.sort(fileList, Comparator.comparingLong(File::lastModified));
      }

      for (File file : fileList) {
        if (file.isFile()) { // 파일인 경우만
          if (extension.equals("*") || file.getName().endsWith("." + extension)) {

            fileListPos = fileListPos + 1;

            if (startPos <= fileListPos && endPos >= fileListPos) {

              // 생성자를 통해 객체를 만들고 리스트에 추가
              String fileName = file.getName();
              String parentPath = file.getParent();
              String fullPath = file.getAbsolutePath();
              String ext = FilenameUtils.getExtension(fileName);

              Path path = Paths.get(parentPath);
              String parentFolderNm = path.getFileName().toString();

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

              files.add(
                  new FileDto.Basic(
                      fileName, parentFolderNm, parentPath, fullPath, ext, fileSize, lastModified));

              fileTotCnt = fileTotCnt + 1;
              fileTotSize = fileTotSize + fileSize;
            }
          }
        }
      }
    }

    return new FilesDto(dirPath, fileTotCnt, fileTotSize, files);
  }

  public FilesDto getFilesDepthAll(SrchFilesDepthDto srchDto) {

    Path startPath = Paths.get(srchDto.getDirPath());
    int maxDepth = srchDto.getMaxDepth();
    String dirPath = srchDto.getDirPath();
    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))
              .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 new FilesDto(dirPath, fileTotCnt, fileTotSize, 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;
    }
  }

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

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



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

  @Transactional
  public MapSheetMngDto.DmlReturn mngComplete(int mngYyyy) {

    mapSheetMngCoreService.MapSheetMngComplete(mngYyyy);

    return new MapSheetMngDto.DmlReturn("success", "작업완료 처리되었습니다.");
  }

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

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

  @Transactional
  public MapSheetMngDto.DmlReturn mngDataSave(MapSheetMngDto.AddReq AddReq) {
    return mapSheetMngCoreService.mngDataSave(AddReq);
  }

  /*
  public MapSheetMngDto.DmlReturn uploadFile(MultipartFile file, Long hstUid) {
    return mapSheetMngCoreService.uploadFile(file, hstUid);
  }

  public MapSheetMngDto.DmlReturn deleteFile(MapSheetMngDto.DeleteFileReq req) {
    return mapSheetMngCoreService.deleteFile(req);
  }

   */

  public MapSheetMngDto.DmlReturn uploadProcess(@Valid List<Long> hstUidList) {
    return mapSheetMngCoreService.uploadProcess(hstUidList);
  }

  public MapSheetMngDto.DmlReturn updateExceptUseInference(@Valid List<Long> hstUidList) {
    return mapSheetMngCoreService.updateExceptUseInference(hstUidList);
  }

  @Transactional
  public DmlReturn uploadPair(
      MultipartFile tfwFile,
      MultipartFile tifFile,
      // String targetPath,
      Long hstUid) {

    // 파일 유효성 검증
    if (tfwFile == null || tfwFile.isEmpty() || tfwFile.getSize() == 0) {
      return new DmlReturn("fail", "TFW SIZE 오류");
    } else if (tifFile == null || tifFile.isEmpty() || tifFile.getSize() == 0) {
      return new DmlReturn("fail", "TIF SIZE 오류");
    }

    if (!tfwFile
        .getOriginalFilename()
        .substring(tfwFile.getOriginalFilename().lastIndexOf('.') + 1)
        .toLowerCase()
        .equals("tfw")) {
      return new DmlReturn("fail", "TFW TYPE ERROR");
    } else if (!tifFile
        .getOriginalFilename()
        .substring(tifFile.getOriginalFilename().lastIndexOf('.') + 1)
        .toLowerCase()
        .equals("tif")) {
      return new DmlReturn("fail", "TIF TYPE ERROR");
    }

    // 싱크파일목록 가저오기
    List<MngFilesDto> mngFiles = mapSheetMngCoreService.findIdToMapSheetFileList(hstUid);
    String uploadPath = "";
    for (MngFilesDto dto : mngFiles) {
      uploadPath = dto.getFilePath();
      break;
    }

    if (uploadPath.isEmpty()) {
      // ErrorDataDto errDto = mapSheetMngCoreService.findMapSheetError(hstUid);
      // uploadPath = FileConfig.rootSyncDir+"\\"+errDto.getMapSheetNum();
    }

    // tfw가 있는 경우
    // tif가 있는 경우

    return new DmlReturn("success", "파일 업로드 완료되었습니다.");
  }

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

  @Transactional
  public MapSheetMngDto.DmlReturn deleteByFileUidMngFile(List<Long> fileUids) {

    for (Long uid : fileUids) {
      MapSheetMngDto.DmlReturn dmlReturn = mapSheetMngCoreService.deleteByFileUidMngFile(uid);
    }

    return new MapSheetMngDto.DmlReturn("success", fileUids.size() + "개 파일이 삭제되었습니다.");
  }
}
