package top.r3944realms.docchecktoolrefactored.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

/**
 * 文件工具类
 */
@Slf4j
public class FileUtil {
    /**
     * 将字节数转换为易读的格式
     */
    public static String humanReadableByteCount(long bytes) {
        if (bytes < 0) throw new IllegalArgumentException("字节数不能为负数");
        if (bytes < 1024) return bytes + " B";
        int exp = (int) (Math.log(bytes) / Math.log(1024));
        String unit = "KMGTPE".charAt(exp-1) + "iB";
        return String.format(Locale.US,"%.1f %s", bytes / Math.pow(1024, exp), unit);
    }

    public static String getFileExtension(String filePath) throws NoSuchFileException {
        Path path = Paths.get(filePath).normalize();
        File file = path.toFile();
        if (!file.exists()) {
            log.error(LoggerMarker.TRACE_MARKER, "文件不存在: {}", filePath);
            throw new NoSuchFileException("文件不存在: " + filePath);
        }

        String fileName = path.getFileName().toString();
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
            throw new IllegalArgumentException("文件无有效扩展名: " + fileName);
        }
        return fileName.substring(dotIndex + 1).toLowerCase();
    }

    /**
     * 检查文件路径并返回
     *
     * @param filePath 文件路径
     * @return File
     * @throws NoSuchFileException 当指定路径无此文件时抛出
     */
    public static File fileCheckAndGet(String filePath) throws NoSuchFileException {
        return fileCheckAndGet(filePath, (String) null);
    }

    /**
     * 检查文件路径并返回
     *
     * @param filePath 文件路径
     * @param supportedFileExtensions 文件扩展名，例如 ["pdf", "txt]
     * @throws IllegalArgumentException 当指定路径文件无扩展名（如README），或扩展名不符时抛出
     * @throws NoSuchFileException 当指定路径无此文件时抛出
     */
    public static File fileCheckAndGet(String filePath, String... supportedFileExtensions)
            throws NoSuchFileException, IllegalArgumentException {

        Path path = Paths.get(filePath).normalize();
        File file = path.toFile();

        if (!file.exists()) {
            log.error(LoggerMarker.TRACE_MARKER, "文件不存在: {}", filePath);
            throw new NoSuchFileException("文件不存在: " + filePath);
        }

        if (supportedFileExtensions == null || supportedFileExtensions.length == 0) {
            return file;
        }

        String fileName = path.getFileName().toString();
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
            throw new IllegalArgumentException("文件无有效扩展名: " + fileName);
        }

        String fileExtension = fileName.substring(dotIndex + 1).toLowerCase();
        List<String> supportedExtensions = Arrays.stream(supportedFileExtensions)
                .map(String::toLowerCase)
                .toList();

        if (!supportedExtensions.contains(fileExtension)) {
            log.error(LoggerMarker.TRACE_MARKER, "不支持的文件格式: {}", fileExtension);
            throw new IllegalArgumentException("不支持的文件格式，预期: "
                    + supportedExtensions + "，实际: " + fileExtension);
        }

        return file;
    }
    /**
     * 检查文件名是否合法（跨平台）
     * @param fileName 文件名（不含路径）
     * @return true 合法；false 非法
     */
    public static boolean isValidFileName(String fileName) {
        if (fileName == null || fileName.isBlank()) return false;

        // Windows 非法字符
        String invalidChars = "<>:\"/\\\\|?*";
        for (char c : invalidChars.toCharArray()) {
            if (fileName.indexOf(c) >= 0) return false;
        }

        // Windows 保留名称
        List<String> reservedNames = Arrays.asList(
                "CON", "PRN", "AUX", "NUL",
                "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
                "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
        );
        String upperName = fileName.toUpperCase(Locale.ROOT);
        String nameWithoutExt = upperName.contains(".") ? upperName.substring(0, upperName.indexOf('.')) : upperName;
        if (reservedNames.contains(nameWithoutExt)) return false;

        // 不能以空格或句点结尾
        if (fileName.endsWith(" ") || fileName.endsWith(".")) return false;

        return true;
    }

    /**
     * 自动修复非法文件名：
     *  - 替换非法字符为下划线
     *  - 去除末尾空格和句点
     *  - 避免使用系统保留名
     * @param fileName 原始文件名
     * @return 修复后的安全文件名
     */
    public static String sanitizeFileName(String fileName) {
        if (fileName == null || fileName.isBlank()) {
            return "untitled";
        }

        // 替换非法字符为下划线
        String sanitized = fileName.replaceAll("[<>:\"/\\\\|?*]", "_");

        // 去掉结尾空格和句点
        sanitized = sanitized.replaceAll("[ \\.]+$", "");

        // Windows 保留名修复
        List<String> reservedNames = Arrays.asList(
                "CON", "PRN", "AUX", "NUL",
                "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
                "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
        );
        String upperName = sanitized.toUpperCase(Locale.ROOT);
        String nameWithoutExt = upperName.contains(".") ? upperName.substring(0, upperName.indexOf('.')) : upperName;
        if (reservedNames.contains(nameWithoutExt)) {
            sanitized = "_" + sanitized; // 添加前缀防止冲突
        }

        // 如果修复后为空，则命名为 untitled
        if (sanitized.isBlank()) sanitized = "untitled";

        return sanitized;
    }

    /**
     * 校验或修复文件名：
     * 若合法则直接返回；
     * 若非法则自动修复并打印日志
     */
    public static String ensureValidFileName(String fileName) {
        if (isValidFileName(fileName)) {
            return fileName;
        }
        String sanitized = sanitizeFileName(fileName);
        log.warn(LoggerMarker.TRACE_MARKER, "非法文件名已修复: {} → {}", fileName, sanitized);
        return sanitized;
    }
}
