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

import static java.lang.String.CASE_INSENSITIVE_ORDER;

import com.kamco.cd.kamcoback.common.exception.DuplicateFileException;
import com.kamco.cd.kamcoback.common.exception.ValidationException;
import com.kamco.cd.kamcoback.common.utils.FIleChecker;
import com.kamco.cd.kamcoback.common.utils.NameValidator;
import com.kamco.cd.kamcoback.config.FileConfig;
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.FolderDto;
import com.kamco.cd.kamcoback.mapsheet.dto.FileDto.FoldersDto;
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.FileDto.SrchFoldersDto;
import com.kamco.cd.kamcoback.mapsheet.dto.ImageryDto;
import com.kamco.cd.kamcoback.postgres.core.MapSheetMngFileCheckerCoreService;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngFileEntity;
import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngFileRepository;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

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

  private final MapSheetMngFileCheckerCoreService mapSheetMngFileCheckerCoreService;
  private final FileConfig fileConfig;
  private final MapSheetMngFileRepository mapSheetMngFileRepository;

  // @Value("${mapsheet.upload.skipGdalValidation:false}")
  // private boolean skipGdalValidation;

  public FoldersDto getFolderAll(SrchFoldersDto srchDto) {

    Path startPath = Paths.get(fileConfig.getRootSyncDir() + srchDto.getDirPath());
    String dirPath = fileConfig.getRootSyncDir() + srchDto.getDirPath();

    // Path startPath = Paths.get(fileConfig.getRootSyncDir()+srchDto.getDirPath());
    // String dirPath =  fileConfig.getRootSyncDir()+srchDto.getDirPath();

    int maxDepth = 1;

    int folderTotCnt = 0;
    int folderErrTotCnt = 0;

    List<FolderDto> folderDtoList = List.of();
    SimpleDateFormat dttmFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

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

      folderDtoList =
          stream
              // 1. 디렉토리만 필터링
              .filter(Files::isDirectory)
              .filter(p -> !p.toString().equals(dirPath))
              .map(
                  path -> {
                    int depth = path.getNameCount();

                    String folderNm = path.getFileName().toString();
                    String parentFolderNm = path.getParent().getFileName().toString();
                    String parentPath = path.getParent().toString();
                    String fullPath = path.toAbsolutePath().toString();

                    boolean isValid =
                        !NameValidator.containsKorean(folderNm)
                            && !NameValidator.containsWhitespaceRegex(folderNm);

                    File directory = new File(fullPath);
                    File[] childFolders = directory.listFiles(File::isDirectory);

                    long childCnt = 0;
                    if (childFolders != null) {
                      childCnt = childFolders.length;
                    }

                    FileTime time = null;
                    String lastModified = "";
                    try {
                      time = Files.getLastModifiedTime(path);
                      lastModified = dttmFormat.format(new Date(time.toMillis()));
                    } catch (IOException e) {
                      throw new RuntimeException(e);
                    }

                    return new FolderDto(
                        folderNm,
                        parentFolderNm,
                        parentPath,
                        fullPath,
                        depth,
                        childCnt,
                        lastModified,
                        isValid);
                  })
              .collect(Collectors.toList());

      folderDtoList.sort(
          Comparator.comparing(
                  FolderDto::getFolderNm, CASE_INSENSITIVE_ORDER // 대소문자 구분 없이
                  )
              .reversed());

      folderTotCnt = folderDtoList.size();
      folderErrTotCnt =
          (int)
              folderDtoList.stream()
                  .filter(dto -> dto.getIsValid().toString().equals("false"))
                  .count();

    } catch (IOException e) {
      throw new RuntimeException(e);
    }

    return new FoldersDto(dirPath, folderTotCnt, folderErrTotCnt, folderDtoList);
  }

  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 ImageryDto.SyncReturn syncProcess(ImageryDto.searchReq searchReq) {
    return mapSheetMngFileCheckerCoreService.syncProcess(searchReq);
  }

  @Transactional
  public String uploadFile(MultipartFile file, String targetPath, boolean overwrite, Long hstUid) {
    try {
      // 파일 유효성 검증
      if (file == null || file.isEmpty()) {
        throw new ValidationException("업로드 파일이 비어있습니다.");
      }
      if (file.getOriginalFilename() == null || file.getOriginalFilename().isEmpty()) {
        throw new ValidationException("파일명이 유효하지 않습니다.");
      }

      Path path = Paths.get(targetPath);

      // targetPath가 존재하지 않으면 파일 경로로 가정하고 부모 디렉토리 생성
      if (!Files.exists(path)) {
        // 경로가 확장자로 끝나면 파일로 간주
        if (targetPath.matches(".*\\.[a-zA-Z]{3,4}$")) {
          if (path.getParent() != null) {
            Files.createDirectories(path.getParent());
          }
        } else {
          // 확장자가 없으면 디렉토리로 간주
          Files.createDirectories(path);
          path = path.resolve(file.getOriginalFilename());
        }
      } else if (Files.isDirectory(path)) {
        path = path.resolve(file.getOriginalFilename());
      }

      // 최종 파일의 부모 디렉토리 생성
      if (path.getParent() != null && !Files.exists(path.getParent())) {
        Files.createDirectories(path.getParent());
      }

      String filename = path.getFileName().toString();
      String ext = FilenameUtils.getExtension(filename).toLowerCase();
      String baseName = FilenameUtils.getBaseName(filename);
      Path tfwPath =
          path.getParent() == null
              ? Paths.get(baseName + ".tfw")
              : path.getParent().resolve(baseName + ".tfw");
      Path tifPath =
          path.getParent() == null
              ? Paths.get(baseName + ".tif")
              : path.getParent().resolve(baseName + ".tif");

      // DB 중복 체크
      String parentPathStr = path.getParent() != null ? path.getParent().toString() : "";
      boolean dbExists =
          mapSheetMngFileRepository.existsByFileNameAndFilePath(filename, parentPathStr);
      // boolean fileExists = Files.exists(path); // 파일 시스템 존재 여부는 체크하지 않음 (DB 기준)

      // 이미 존재하는 경우 처리 (DB에만 있는 경우 체크)
      if (!overwrite && dbExists) {
        throw new DuplicateFileException("동일한 파일이 이미 존재합니다 (DB): " + path.getFileName());
      }

      // 덮어쓰기인 경우 기존 DB 데이터 삭제 (새로 저장하기 위함)
      if (overwrite && dbExists) {
        mapSheetMngFileRepository.deleteByFileNameAndFilePath(filename, parentPathStr);
      }

      // 업로드 파일 저장(덮어쓰기 허용 시 replace)
      file.transferTo(path.toFile());

      if ("tfw".equals(ext)) {
        // TFW 검증
        boolean tfwOk = FIleChecker.checkTfw(path.toString());
        if (!tfwOk) {
          Files.deleteIfExists(path);
          throw new ValidationException(
              "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + path.getFileName());
        }
        // 안내: 같은 베이스의 TIF가 없으면 추후 TIF 업로드 필요
        if (!Files.exists(tifPath)) {
          // DB 메타 저장은 진행 (향후 쌍 검증 위해)
          saveUploadMeta(path, hstUid);
          return "TFW 업로드 성공 (매칭되는 TIF가 아직 없습니다).";
        }
        // TIF가 존재하면 쌍 요건 충족
        saveUploadMeta(path, hstUid);
        return "TFW 업로드 성공";
      }

      if ("tif".equals(ext) || "tiff".equals(ext)) {
        // GDAL 검증 (항상 수행)
        boolean isValidTif = FIleChecker.cmmndGdalInfo(path.toString());
        if (!isValidTif) {
          Files.deleteIfExists(path);
          throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + path.getFileName());
        }

        // TFW 존재/검증
        if (!Files.exists(tfwPath)) {
          Files.deleteIfExists(path);
          throw new ValidationException("TFW 파일이 존재하지 않습니다: " + tfwPath.getFileName());
        }
        boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString());
        if (!tfwOk) {
          Files.deleteIfExists(path);
          throw new ValidationException(
              "유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwPath.getFileName());
        }
        saveUploadMeta(path, hstUid);
        return "TIF 업로드 성공";
      }

      // 기타 확장자: 저장만 하고 메타 기록
      saveUploadMeta(path, hstUid);

      return "업로드 성공";
    } catch (ValidationException | DuplicateFileException e) {
      // 비즈니스 예외는 그대로 던짐
      throw e;
    } catch (IOException e) {
      throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e);
    } catch (Exception e) {
      throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e);
    }
  }

  @Transactional
  public String uploadPair(
      MultipartFile tfwFile,
      MultipartFile tifFile,
      String targetPath,
      boolean overwrite,
      Long hstUid) {
    try {
      log.info(
          "uploadPair 시작 - targetPath: {}, overwrite: {}, hstUid: {}",
          targetPath,
          overwrite,
          hstUid);

      // 파일 유효성 검증
      if (tfwFile == null || tfwFile.isEmpty()) {
        throw new ValidationException("TFW 파일이 비어있습니다.");
      }
      if (tifFile == null || tifFile.isEmpty()) {
        throw new ValidationException("TIF 파일이 비어있습니다.");
      }
      if (tfwFile.getOriginalFilename() == null || tfwFile.getOriginalFilename().isEmpty()) {
        throw new ValidationException("TFW 파일명이 유효하지 않습니다.");
      }
      if (tifFile.getOriginalFilename() == null || tifFile.getOriginalFilename().isEmpty()) {
        throw new ValidationException("TIF 파일명이 유효하지 않습니다.");
      }

      log.info(
          "파일명 - TFW: {}, TIF: {}", tfwFile.getOriginalFilename(), tifFile.getOriginalFilename());

      Path basePath = Paths.get(targetPath);

      // targetPath가 존재하지 않으면 디렉토리로 생성
      if (!Files.exists(basePath)) {
        log.info("대상 경로가 존재하지 않아 디렉토리 생성: {}", basePath);
        Files.createDirectories(basePath);
      }

      // 파일인 경우 부모 디렉토리를 basePath로 사용
      if (Files.isRegularFile(basePath)) {
        log.info("대상 경로가 파일이므로 부모 디렉토리 사용");
        basePath = basePath.getParent();
      }

      if (Files.isDirectory(basePath)) {
        log.info("디렉토리 확인됨: {}", basePath);
        // 디렉토리인 경우 파일명 기준으로 경로 생성
        Path tfwPath = basePath.resolve(tfwFile.getOriginalFilename());
        Path tifPath = basePath.resolve(tifFile.getOriginalFilename());
        // 동일 베이스명 확인
        String tfwBase = FilenameUtils.getBaseName(tfwPath.getFileName().toString());
        String tifBase = FilenameUtils.getBaseName(tifPath.getFileName().toString());
        if (!tfwBase.equalsIgnoreCase(tifBase)) {
          throw new ValidationException("TFW/TIF 파일명이 동일한 베이스가 아닙니다.");
        }
        // 디렉토리는 이미 생성되었으므로 추가 생성 불필요
        // if (tfwPath.getParent() != null) Files.createDirectories(tfwPath.getParent());
        // if (tifPath.getParent() != null) Files.createDirectories(tifPath.getParent());

        // DB 중복 체크 및 overwrite 처리 (각 파일별)
        String parentPathStr = basePath.toString();
        String tfwName = tfwPath.getFileName().toString();
        String tifName = tifPath.getFileName().toString();
        boolean tfwDbExists =
            mapSheetMngFileRepository.existsByFileNameAndFilePath(tfwName, parentPathStr);
        boolean tifDbExists =
            mapSheetMngFileRepository.existsByFileNameAndFilePath(tifName, parentPathStr);
        if (!overwrite && (tfwDbExists || tifDbExists)) {
          throw new DuplicateFileException("동일한 파일이 이미 존재합니다 (DB): " + tfwName + ", " + tifName);
        }
        if (overwrite) {
          if (tfwDbExists)
            mapSheetMngFileRepository.deleteByFileNameAndFilePath(tfwName, parentPathStr);
          if (tifDbExists)
            mapSheetMngFileRepository.deleteByFileNameAndFilePath(tifName, parentPathStr);
        }

        // 파일 저장
        log.info("파일 저장 시작 - TFW: {}, TIF: {}", tfwPath, tifPath);
        tfwFile.transferTo(tfwPath.toFile());
        tifFile.transferTo(tifPath.toFile());
        log.info("파일 저장 완료");

        // 검증
        log.info("TFW 파일 검증 시작: {}", tfwPath);
        boolean tfwOk = FIleChecker.checkTfw(tfwPath.toString());
        if (!tfwOk) {
          log.warn("TFW 파일 검증 실패: {}", tfwName);
          Files.deleteIfExists(tfwPath);
          Files.deleteIfExists(tifPath);
          throw new ValidationException("유효하지 않은 TFW 파일입니다 (6줄 숫자 형식 검증 실패): " + tfwName);
        }
        log.info("TFW 파일 검증 성공");

        log.info("TIF 파일 검증 시작: {}", tifPath);
        boolean isValidTif = FIleChecker.cmmndGdalInfo(tifPath.toString());
        if (!isValidTif) {
          log.warn("TIF 파일 검증 실패: {}", tifName);
          Files.deleteIfExists(tfwPath);
          Files.deleteIfExists(tifPath);
          throw new ValidationException("유효하지 않은 TIF 파일입니다 (GDAL 검증 실패): " + tifName);
        }
        log.info("TIF 파일 검증 성공");

        // 메타 저장 (두 파일 각각 저장)
        log.info("메타 데이터 저장 시작");
        saveUploadMeta(tfwPath, hstUid);
        saveUploadMeta(tifPath, hstUid);
        log.info("메타 데이터 저장 완료");
        return "TFW/TIF 페어 업로드 성공";
      } else {
        throw new ValidationException("targetPath는 디렉토리여야 합니다.");
      }
    } catch (ValidationException | DuplicateFileException e) {
      // 비즈니스 예외는 그대로 던짐
      log.warn("업로드 비즈니스 예외 발생: {}", e.getMessage());
      throw e;
    } catch (IOException e) {
      log.error("파일 I/O 처리 실패: {}", e.getMessage(), e);
      throw new IllegalArgumentException("파일 I/O 처리 실패: " + e.getMessage(), e);
    } catch (Exception e) {
      log.error("파일 업로드 처리 중 예상치 못한 오류 발생: {}", e.getMessage(), e);
      throw new IllegalArgumentException("파일 업로드 처리 중 오류 발생: " + e.getMessage(), e);
    }
  }

  private void saveUploadMeta(Path savedPath, Long hstUid) {
    String fullPath = savedPath.toAbsolutePath().toString();
    String fileName = savedPath.getFileName().toString();
    String ext = FilenameUtils.getExtension(fileName);

    MapSheetMngFileEntity entity = new MapSheetMngFileEntity();

    if (hstUid != null) {
      // 히스토리에서 메타 가져오기
      var hstOpt = mapSheetMngFileCheckerCoreService.findHstByUid(hstUid);
      hstOpt.ifPresent(
          hst -> {
            entity.setHstUid(hst.getHstUid());
            entity.setMngYyyy(hst.getMngYyyy());
            entity.setMapSheetNum(hst.getMapSheetNum());
            entity.setRefMapSheetNum(hst.getRefMapSheetNum());
          });
    } else {
      // 기존 추정 로직 유지
      Integer mngYyyy = extractYearFromPath(fullPath);
      String mapSheetNum = extractMapSheetNumFromFileName(fileName);
      String refMapSheetNum = null;
      if (mapSheetNum != null && !mapSheetNum.isEmpty()) {
        try {
          long num = Long.parseLong(mapSheetNum);
          refMapSheetNum = String.valueOf(num / 1000);
        } catch (NumberFormatException ignored) {
        }
      }
      entity.setMngYyyy(mngYyyy);
      entity.setMapSheetNum(mapSheetNum);
      entity.setRefMapSheetNum(refMapSheetNum);
    }

    entity.setFilePath(savedPath.getParent() != null ? savedPath.getParent().toString() : "");
    entity.setFileName(fileName);
    entity.setFileExt(ext);

    // 파일 크기 설정
    try {
      long size = Files.size(savedPath);
      entity.setFileSize(size);
    } catch (IOException e) {
      entity.setFileSize(0L);
    }

    mapSheetMngFileRepository.save(entity);
  }

  private Integer extractYearFromPath(String fullPath) {
    // 경로에서 4자리 연도를 찾아 가장 근접한 값을 사용
    // 예시 경로: /Users/.../original-images/2022/2022_25cm/1/34602
    String[] parts = fullPath.split("/");
    for (String p : parts) {
      if (p.matches("\\d{4}")) {
        try {
          return Integer.parseInt(p);
        } catch (NumberFormatException ignored) {
        }
      }
    }
    return null;
  }

  private String extractMapSheetNumFromFileName(String fileName) {
    // 파일명에서 연속된 숫자를 최대한 찾아 사용 (예: 34602027.tif -> 34602027)
    String base = FilenameUtils.getBaseName(fileName);
    String digits = base.replaceAll("[^0-9]", "");
    if (!digits.isEmpty()) {
      return digits;
    }
    return null;
  }

  @Transactional
  public Boolean deleteFile(String filePath) {
    try {
      Path path = Paths.get(filePath);
      return Files.deleteIfExists(path);
    } catch (IOException e) {
      throw new RuntimeException("파일 삭제 실패: " + e.getMessage());
    }
  }

  @Transactional(readOnly = true)
  public List<MapSheetMngFileEntity> findRecentFiles(int limit) {
    // 간단히 전체를 불러 정렬/제한 (운영에선 Page 요청으로 변경 권장)
    List<MapSheetMngFileEntity> all = new ArrayList<>();
    mapSheetMngFileRepository.findAll().forEach(all::add);
    all.sort(
        (a, b) -> {
          // fileUid 기준 내림차순
          long av = a.getFileUid() == null ? 0L : a.getFileUid();
          long bv = b.getFileUid() == null ? 0L : b.getFileUid();
          return Long.compare(bv, av);
        });
    if (all.size() > limit) {
      return all.subList(0, limit);
    }
    return all;
  }

  @Transactional
  public String deleteDuplicate(String filePath, String fileName) {
    try {
      Path path = Paths.get(filePath, fileName);
      boolean deleted = Files.deleteIfExists(path);
      // DB에서도 삭제
      mapSheetMngFileRepository.deleteByFileNameAndFilePath(fileName, filePath);
      return deleted ? "파일 및 DB 레코드 삭제 완료" : "DB 레코드 삭제 완료 (파일 미존재)";
    } catch (IOException e) {
      throw new RuntimeException("중복 파일 삭제 실패: " + e.getMessage());
    }
  }
}
