package top.r3944realms.docchecktoolrefactored.core;

import lombok.extern.slf4j.Slf4j;
import top.r3944realms.docchecktoolrefactored.io.reader.CatalogFileReader;
import top.r3944realms.docchecktoolrefactored.io.reader.CatalogFileReaderFactory;
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.List;
import java.util.Set;

@Slf4j
public class LogicalAddressFileGenerator 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 catalogFilePath, File outputFile, int folderType) {
        safeOnPhaseStarted(Phase.GENERATE_LOGICAL);
        try {
            // 使用工厂模式创建相应的文件读取器
            List<Record> records = readCatalogFile(catalogFilePath);
            safeOnPhaseProgress(Phase.GENERATE_LOGICAL, 1, 3); // 1/3
            // 过滤掉 可能的 输出文件记录 -- 注2: 避免将输出文件作为输入处理
            String outputFileName = outputFile.getName();
            records = records.stream()
                    .filter(record -> !record.archiveCode.equalsIgnoreCase(outputFileName))
                    .toList();
            safeOnPhaseProgress(Phase.GENERATE_LOGICAL, 2, 3); // 2/3
            try (PrintWriter writer = new PrintWriter(outputFile, StandardCharsets.UTF_8)) {
                if (folderType == PAGE_TYPE) {
                    // 页面级逻辑：为每页生成一行数据
                    generatePageLevelFile(writer, records, callback);
                } else if (folderType == FILE_TYPE) {
                    // 文件级逻辑：每个档案只生成一行数据
                    generateFileLevelFile(writer, records, callback);
                } else {
                    throw new IllegalArgumentException("不支持的文件夹类型: " + folderType);
                }
            }
            safeOnPhaseProgress(Phase.GENERATE_LOGICAL, 3, 3); // 3/3
            safeOnPhaseCompleted(Phase.GENERATE_LOGICAL);
            safeOnPhaseCompleted(Phase.GENERATE_LOGICAL);
        } catch (RuntimeException e) {
            // 直接重新抛出自定义的重复档号异常，不让它被日志吞掉
            if (e.getMessage() != null && e.getMessage().startsWith("存在重复档号：")) {
                throw e;
            }
            log.info(LoggerMarker.RELEASE_MARKER, "生成逻辑地址文件时出错: {}", e.getMessage());
            throw e;
        } catch (Exception e) {
            log.info(LoggerMarker.RELEASE_MARKER, "生成逻辑地址文件时出错: {}", e.getMessage());
            throw new RuntimeException(e);
        }
    }
    /**
     * 生成页面级逻辑地址文件
     */
    private void generatePageLevelFile(PrintWriter writer, List<Record> records, ProgressCallback callback) {
        Set<String> seenCodes = new HashSet<>();
        Set<String> duplicateCodes = new HashSet<>();
        // 收集所有重复的档号
        for (Record record : records) {
            String archiveCode = record.archiveCode;
            if (!seenCodes.add(archiveCode)) {
                duplicateCodes.add(archiveCode);
            }
        }

        // 如果存在重复档号，记录日志并抛出异常
        if (!duplicateCodes.isEmpty()) {
            StringBuilder errorMsg = new StringBuilder("存在重复档号：\n");
            for (String code : duplicateCodes) {
                errorMsg.append(code).append("\n");
            }
            log.info(LoggerMarker.RELEASE_MARKER, "{}", errorMsg.toString());

            throw new RuntimeException(errorMsg.toString());
        }else {
            // 写入CSV头部
            writer.println("逻辑文件名,逻辑地址");
            int totalRecords = records.stream().mapToInt(r -> r.page).sum();
            int current = 0;

            // 处理每条记录
            for (Record record : records) {
                String archiveCode = record.archiveCode;

                int page = record.page;

                // 为每页生成一行数据
                for (int i = 1; i <= page; i++) {
                    // 生成逻辑文件名
                    String logicalFileName = String.format("%s-%04d", archiveCode, i);

                    // 生成逻辑地址
                    String logicalAddress = generatePageLevelLogicalAddress(archiveCode, i);

                    // 写入CSV行
                    writer.printf("%s,%s%n", logicalFileName, logicalAddress);
                    current++;
                    safeOnPhaseProgress(Phase.GENERATE_LOGICAL, current, totalRecords);
                }
            }
        }
    }

    /**
     * 生成文件级逻辑地址文件
     */
    private void generateFileLevelFile(PrintWriter writer, List<Record> records, ProgressCallback callback) {
        Set<String> seenCodes = new HashSet<>();
        Set<String> duplicateCodes = new HashSet<>();
        // 收集所有重复的档号
        for (Record record : records) {
            String archiveCode = record.archiveCode;
            if (!seenCodes.add(archiveCode)) {
                duplicateCodes.add(archiveCode);
            }
        }

        // 如果存在重复档号，记录日志并抛出异常
        if (!duplicateCodes.isEmpty()) {
            StringBuilder errorMsg = new StringBuilder("存在重复档号：\n");
            for (String code : duplicateCodes) {
                errorMsg.append(code).append("\n");
            }
            log.info(LoggerMarker.RELEASE_MARKER, "{}", errorMsg.toString());

            throw new RuntimeException(errorMsg.toString());
        } else {
            // 写入CSV头部（包含页数列）
            writer.println("逻辑文件名,逻辑地址,页数");
            int totalRecords = records.stream().mapToInt(r -> r.page).sum();
            int current = 0;

            // 处理每条记录
            for (Record record : records) {
                String archiveCode = record.archiveCode;
                int page = record.page;

                // 生成逻辑地址（不包含页数）
                String logicalAddress = generateFileLevelLogicalAddress(archiveCode);

                // 写入CSV行，包含页数
                writer.printf("%s,%s,%d%n", /* 逻辑文件名（就是档号）*/ archiveCode, logicalAddress, page);
                safeOnPhaseProgress(Phase.GENERATE_LOGICAL, current, totalRecords);
            }
        }
    }

    /**
     * 根据档号生成文件级逻辑地址（不包含页数）
     *
     * @param archiveCode 档号内容
     * @return 文件级逻辑地址
     */
    private String generateFileLevelLogicalAddress(String archiveCode) {
        StringBuilder address = csvStringBuilder(archiveCode);

        // 添加最后的档号部分
        if (!address.isEmpty()) {
            address.append("\\").append(archiveCode);
        }

        return address.toString();
    }


    /**
     * 根据档号生成逻辑地址
     *
     * @param archiveCode 档号内容
     * @param pageNumber 页码
     * @return 逻辑地址
     */
    private String generatePageLevelLogicalAddress(String archiveCode, int pageNumber) {
        StringBuilder address = csvStringBuilder(archiveCode);

        // 添加页码部分，同样去掉第一个
        if (!address.isEmpty()) {
            address.append("\\");
        }
        address.append(String.format("%s-%04d", archiveCode, pageNumber));

        return address.toString();
    }

    private StringBuilder csvStringBuilder(String archiveCode) {
        StringBuilder address = new StringBuilder();
        String[] parts = archiveCode.split("-");

        StringBuilder path = new StringBuilder();
        for (int i = 0; i < parts.length; i++) {
            if (i > 0) {
                path.append("-");
            }
            path.append(parts[i]);
            // 去掉第一个 \
            if (address.isEmpty()) {
                address.append(path);
            } else {
                address.append("\\").append(path);
            }
        }
        return address;
    }

    /**
     * 读取目录文件
     *
     * @param filePath 文件路径
     * @return 记录列表
     * @throws Exception 读取异常
     */
    private List<Record> readCatalogFile(String filePath) throws Exception {
        // 使用工厂模式创建相应的文件读取器
        CatalogFileReader reader = CatalogFileReaderFactory.createReader(filePath);
        return reader.readCatalogFile(filePath);
    }
    public record Record(String archiveCode, int page) {
    }
}
