package top.r3944realms.docchecktoolrefactored.core;

import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.ofdrw.reader.OFDReader;
import top.r3944realms.docchecktoolrefactored.util.LoggerMarker;

import java.io.File;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

@Slf4j
public class PhysicalAddressFileGenerator implements AddressFileGenerator {
    private ProgressCallback callback;
    // 安全调用回调方法
    private void safeOnPhaseStarted(Phase phase) {
        if (callback != null) {
            callback.onPhaseStarted(phase);
        }
    }

    private void safeOnPhaseProgress(Phase phase, int current, int total) {
        if (callback != null) {
            callback.onPhaseProgress(phase, current, total);
        }
    }

    private void safeOnPhaseCompleted(Phase phase) {
        if (callback != null) {
            callback.onPhaseCompleted(phase);
        }
    }

    @Override
    public void setProgressCallback(ProgressCallback callback) {
        this.callback = callback;
    }

    @Override
    public void generateAddressFile(String folderPath, File outputFile, int folderType) {
        safeOnPhaseStarted(Phase.GENERATE_PHYSICAL);
        try {
            File rootFolder = new File(folderPath);
            if (!rootFolder.exists() || !rootFolder.isDirectory()) {
                safeOnPhaseCompleted(Phase.GENERATE_PHYSICAL);
                safeOnPhaseProgress(Phase.GENERATE_PHYSICAL, 0, 0);
                return;
            }

            // 统计总文件数（页面级：图片数；文件级：PDF数）
            int totalFiles = countFiles(rootFolder, folderType);

            // 计数器
            int[] counter = new int[]{0};

            try (PrintWriter writer = new PrintWriter(outputFile, StandardCharsets.UTF_8)) {
                if (folderType == PAGE_TYPE) {
                    processPageLevelFolder(rootFolder, writer, outputFile.getAbsolutePath(), callback, counter, totalFiles);
                } else if (folderType == FILE_TYPE) {
                    processFileLevelFolder(rootFolder, writer, outputFile.getAbsolutePath(), callback, counter, totalFiles);
                } else {
                    throw new IllegalArgumentException("不支持的文件夹类型: " + folderType);
                }
            }

            safeOnPhaseCompleted(Phase.GENERATE_PHYSICAL);
        }  catch (RuntimeException e) {
            // 直接重新抛出自定义的重复文件名异常，不让它被日志吞掉
            if (e.getMessage() != null && e.getMessage().startsWith("存在重复文件名：")) {
                throw e;
            }
            safeOnPhaseCompleted(Phase.GENERATE_PHYSICAL);
            safeOnPhaseProgress(Phase.GENERATE_PHYSICAL, 0, 0);
            log.error("生成物理地址文件失败: {}", e.getMessage(), e);
            throw e; // 重新抛出异常，确保能被上层捕获
        }
        catch (Exception e) {
            safeOnPhaseCompleted(Phase.GENERATE_PHYSICAL);
            safeOnPhaseProgress(Phase.GENERATE_PHYSICAL, 0, 0);
            log.error("生成物理地址文件失败: {}", e.getMessage(), e);
        }
    }
    /** 递归统计总文件数 */
    private int countFiles(File folder, int folderType) {
        int count = 0;
        File[] files = folder.listFiles(file -> !file.isHidden());
        if (files != null) {
            for (File file : files) {
                if (file.isFile()) {
                    if ((folderType == PAGE_TYPE && isImageFile(file.getName()))
                            || (folderType == FILE_TYPE && isPdfFile(file.getName()))) {
                        count++;
                    }
                } else if (file.isDirectory()) {
                    count += countFiles(file, folderType);
                }
            }
        }
        return count;
    }

    /**
     * 处理页面级文件夹及其内部文件
     *
     * @param folder 文件夹
     * @param writer PrintWriter对象
     * @param outputFilePath 输出文件的绝对路径
     */
    private void processPageLevelFolder(File folder, PrintWriter writer, String outputFilePath, ProgressCallback callback, int[] counter, int total) {
        Set<String> seenFileNames = new HashSet<>();
        Set<String> duplicateFileNames = new HashSet<>();

        // 收集所有重复的文件名
        collectPageLevelFileNames(folder, outputFilePath, seenFileNames, duplicateFileNames);

        // 如果存在重复文件名，记录日志并抛出异常
        if (!duplicateFileNames.isEmpty()) {
            StringBuilder errorMsg = new StringBuilder("存在重复文件名：\n");
            for (String name : duplicateFileNames) {
                errorMsg.append(name).append("\n");
            }
            log.info(LoggerMarker.RELEASE_MARKER, "{}", errorMsg.toString());
            throw new RuntimeException(errorMsg.toString());
        } else {
            // 写入CSV头部
            writer.println("物理文件名,物理地址");

            // 处理文件（与原来的方法类似，但排除了重复检查）
            processPageLevelFolderInternal(folder, writer, outputFilePath, callback, counter, total);
        }
    }

    /**
     * 递归收集页面级文件夹中的所有文件名以检测重复
     */
    private void collectPageLevelFileNames(File folder, String outputFilePath,
                                           Set<String> seenFileNames, Set<String> duplicateFileNames) {
        File[] filesAndFolders = folder.listFiles(file -> !file.isHidden());

        if (filesAndFolders != null) {
            for (File file : filesAndFolders) {
                // 跳过输出文件本身
                if (file.getAbsolutePath().equals(outputFilePath)) continue;

                if (file.isFile() && isImageFile(file.getName())) {
                    // 移除文件扩展名
                    String fileName = file.getName().substring(0, file.getName().lastIndexOf('.'));
                    // 检查是否重复
                    if (!seenFileNames.add(fileName)) {
                        duplicateFileNames.add(fileName);
                    }
                } else if (file.isDirectory()) {
                    // 递归处理子文件夹
                    collectPageLevelFileNames(file, outputFilePath, seenFileNames, duplicateFileNames);
                }
            }
        }
    }

    /**
     * 实际处理页面级文件夹的内部实现（无重复检查）
     */
    private void processPageLevelFolderInternal(File folder, PrintWriter writer, String outputFilePath,
                                                ProgressCallback callback, int[] counter, int total) {
        // 获取该文件夹下的所有非隐藏文件和文件夹
        File[] filesAndFolders = folder.listFiles(file -> !file.isHidden());

        if (filesAndFolders != null) {
            for (File file : filesAndFolders) {
                // 跳过输出文件本身，避免将生成的CSV文件也作为数据处理
                if (file.getAbsolutePath().equals(outputFilePath)) continue;

                // 只处理图片文件，跳过其他类型的文件
                if (file.isFile() && isImageFile(file.getName())) {
                    // 移除文件扩展名
                    String fileNameWithoutExt = file.getName().substring(0, file.getName().lastIndexOf('.'));

                    // 格式化文件名，确保最后一个部分是4位数字
                    String formattedFileName = formatFileName(fileNameWithoutExt);

                    // 生成物理地址路径
                    String physicalAddress = generatePhysicalAddress(file.getAbsolutePath(), formattedFileName);

                    // 写入CSV行
                    writer.printf("%s,%s%n", formattedFileName, physicalAddress);

                    counter[0]++;
                    safeOnPhaseProgress(Phase.GENERATE_PHYSICAL, counter[0], total);
                } else if (file.isDirectory()) {
                    // 递归处理子文件夹
                    processPageLevelFolderInternal(file, writer, outputFilePath, callback, counter, total);
                }
            }
        }
    }











    /**
     * 处理文件级文件夹（处理PDF文件）
     *
     * @param folder 文件夹
     * @param writer PrintWriter对象
     * @param outputFilePath 输出文件的绝对路径
     */
    private void processFileLevelFolder(File folder, PrintWriter writer, String outputFilePath, ProgressCallback callback, int[] counter, int total) {
        Set<String> seenFileNames = new HashSet<>();
        Set<String> duplicateFileNames = new HashSet<>();

        // 收集所有重复的文件名
        collectFileLevelFileNames(folder, outputFilePath, seenFileNames, duplicateFileNames);

        // 如果存在重复文件名，记录日志并抛出异常
        if (!duplicateFileNames.isEmpty()) {
            StringBuilder errorMsg = new StringBuilder("存在重复文件名：\n");
            for (String name : duplicateFileNames) {
                errorMsg.append(name).append("\n");
            }
            log.info(LoggerMarker.RELEASE_MARKER, "{}", errorMsg.toString());
            throw new RuntimeException(errorMsg.toString());
        } else {
            // 写入CSV头部
            writer.println("物理文件名,物理地址,页数");

            // 处理文件（与原来的方法类似，但排除了重复检查）
            processFileLevelFolderInternal(folder, writer, outputFilePath, callback, counter, total);
        }
    }

    /**
     * 递归收集文件级文件夹中的所有文件名以检测重复
     */
    private void collectFileLevelFileNames(File folder, String outputFilePath,
                                           Set<String> seenFileNames, Set<String> duplicateFileNames) {
        File[] filesAndFolders = folder.listFiles(file -> !file.isHidden());

        if (filesAndFolders != null) {
            for (File file : filesAndFolders) {
                // 跳过输出文件本身
                if (file.getAbsolutePath().equals(outputFilePath)) continue;

                if (file.isFile() && isPdfFile(file.getName())) {
                    String fileName = file.getName().substring(0, file.getName().lastIndexOf('.'));
                    // 检查是否重复
                    if (!seenFileNames.add(fileName)) {
                        duplicateFileNames.add(fileName);
                    }
                } else if (file.isDirectory()) {
                    // 递归处理子文件夹
                    collectFileLevelFileNames(file, outputFilePath, seenFileNames, duplicateFileNames);
                }
            }
        }
    }

    /**
     * 实际处理文件级文件夹的内部实现（无重复检查）
     */
    private void processFileLevelFolderInternal(File folder, PrintWriter writer, String outputFilePath,
                                                ProgressCallback callback, int[] counter, int total) {
        // 获取该文件夹下的所有非隐藏文件和文件夹
        File[] filesAndFolders = folder.listFiles(file -> !file.isHidden());

        if (filesAndFolders != null) {
            for (File file : filesAndFolders) {
                // 跳过输出文件本身，避免将生成的CSV文件也作为数据处理
                if (file.getAbsolutePath().equals(outputFilePath)) {
                    continue;
                }

                if (file.isFile() && isPdfFile(file.getName())) {
                    // 移除文件扩展名
                    String fileNameWithoutExt = file.getName().substring(0, file.getName().lastIndexOf('.'));

                    // 生成物理地址路径（使用与页面级相同的逻辑）
                    String physicalAddress = generatePhysicalAddress(file.getAbsolutePath(), fileNameWithoutExt);

                    // 获取PDF页数
                    int pageCount = getPdfPageCount(file);

                    // 写入CSV行
                    writer.printf("%s,%s,%d%n", fileNameWithoutExt, physicalAddress, pageCount);

                    counter[0]++;
                    safeOnPhaseProgress(Phase.GENERATE_PHYSICAL, counter[0], total);
                } else if (file.isDirectory()) {
                    // 递归处理子文件夹
                    processFileLevelFolderInternal(file, writer, outputFilePath, callback, counter, total);
                }
            }
        }
    }

/**
 * 获取PDF或OFD文件的页数
 *
 * @param file 文件
 * @return 页数
 */
private int getPdfPageCount(File file) {
    try {
        if (file.getName().toLowerCase().endsWith(".pdf")) {
            // 使用Apache PDFBox库获取PDF页数
            try (PDDocument document = Loader.loadPDF(file)) {
                int pageCount = document.getNumberOfPages();
                return pageCount;
            }
        } else if (file.getName().toLowerCase().endsWith(".ofd")) {
             // 使用OFDRW库获取OFD页数
            try (org.ofdrw.reader.OFDReader reader = new org.ofdrw.reader.OFDReader(file.toPath())) {
                // 获取OFD文档的页面数量
                int pageCount = reader.getNumberOfPages();
                return pageCount;
            }
        }
    } catch (Exception e) {
        log.warn(LoggerMarker.RELEASE_MARKER, "无法获取文件页数: {}", file.getAbsolutePath(), e);
        return 0;
    }
    return 0;
}

    /**
     * 判断是否为PDF文件
     *
     * @param fileName 文件名
     * @return 是否为PDF文件
     */
    private boolean isPdfFile(String fileName) {
        return fileName.toLowerCase().endsWith(".pdf")|| fileName.toLowerCase().endsWith(".ofd");
    }

    /**
     * 判断是否为图片文件
     *
     * @param fileName 文件名
     * @return 是否为图片文件
     */
    private boolean isImageFile(String fileName) {
        String lowerFileName = fileName.toLowerCase();
        return lowerFileName.endsWith(".jpg") || lowerFileName.endsWith(".jpeg") ||
                lowerFileName.endsWith(".png") || lowerFileName.endsWith(".bmp") ||
                lowerFileName.endsWith(".gif") || lowerFileName.endsWith(".tiff")||
                lowerFileName.endsWith(".jp2");   // 支持 JPEG 2000 格式
    }

    /**
     * 格式化文件名，确保最后一个部分是4位数字
     *
     * @param fileName 文件名（不含扩展名）
     * @return 格式化后的文件名
     */
    private String formatFileName(String fileName) {
        int lastDashIndex = fileName.lastIndexOf('-');
        if (lastDashIndex != -1 && lastDashIndex < fileName.length() - 1) {
            String lastPart = fileName.substring(lastDashIndex + 1);
            // 检查是否为数字
            if (lastPart.matches("\\d+")) {
                // 格式化为4位数字
                int number = Integer.parseInt(lastPart);
                String formattedLastPart = String.format("%04d", number);
                return fileName.substring(0, lastDashIndex + 1) + formattedLastPart;
            }
        }
        return fileName;
    }

    /**
     * 生成物理地址路径
     *
     * @param absolutePath 文件的绝对路径
     * @param fileName 文件名（不含扩展名）
     * @return 物理地址路径
     */
    private String generatePhysicalAddress(String absolutePath, String fileName) {
        // 获取文件名第一个'-'前面的内容（如9027）
        int firstDashIndex = fileName.indexOf('-');
        String prefix = firstDashIndex != -1 ? fileName.substring(0, firstDashIndex) : fileName;

        // 构建从根路径开始的相对路径
        String relativePath = absolutePath.replace('\\', '/');
        String[] pathParts = relativePath.split("/");

        StringBuilder resultPath = new StringBuilder();
        boolean foundPrefix = false;

        for (int i = 0; i < pathParts.length; i++) {
            if (!foundPrefix) {
                // 检查当前部分是否包含prefix
                if (pathParts[i].equals(prefix)) {
                    foundPrefix = true;
                    resultPath.append(pathParts[i]);
                }
            } else {
                // 去掉文件类型的后缀（如.jpg）
                if (i == pathParts.length - 1) {
                    String fileNameWithoutExt = pathParts[i].substring(0, pathParts[i].lastIndexOf('.'));
                    resultPath.append("/").append(fileNameWithoutExt);
                } else {
                    resultPath.append("/").append(pathParts[i]);
                }
            }
        }

        // 如果结果路径为空，返回空字符串
        if (resultPath.isEmpty()) {
            return "";
        }

        // 将路径中的 / 替换为 \（适用于 Windows 系统）
        return resultPath.toString().replace('/', '\\');
    }
}
