package top.imwork.window.silos.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.List;
/**
 * Copyright (C), 2015-2025
 * FileName: SecurityUtils
 * Author<作者姓名>:   stars
 * CreateTime<创建时间>:   2025/12/15 15:58
 * UpdateTime<修改时间>:   2025/12/15 15:58
 * Description〈功能简述〉: 安全文件检查
 * History<历史描述>:
 * Since<版本号>: 1.0.0
 */
@Slf4j
public class SecurityUtils {

    // 危险文件扩展名
    private static final List<String> DANGEROUS_EXTENSIONS = Arrays.asList(
            ".exe", ".bat", ".cmd", ".ps1", ".vbs", ".js", ".jar",
            ".msi", ".scr", ".pif", ".com", ".sh", ".bash", ".csh",
            ".php", ".php3", ".php4", ".php5", ".phtml", ".py", ".pl",
            ".dll", ".so", ".sys", ".drv"
    );

    // 危险文件MIME类型
    private static final List<String> DANGEROUS_MIME_TYPES = Arrays.asList(
            "application/x-msdownload",
            "application/x-ms-installer",
            "application/x-dosexec",
            "application/x-executable",
            "application/x-shellscript",
            "application/java-archive",
            "application/x-bat",
            "application/x-msdos-program",
            "application/x-sh",
            "application/x-php"
    );

    // 危险文件魔数（文件签名）
    private static final List<byte[]> DANGEROUS_MAGIC_NUMBERS = Arrays.asList(
            new byte[]{(byte)0x4D, (byte)0x5A},          // MZ - Windows EXE
            new byte[]{(byte)0x5A, (byte)0x4D},          // ZM - Windows EXE
            new byte[]{(byte)0x23, (byte)0x21},          // #! - Shell脚本
            new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE}, // Java Class
            new byte[]{(byte)0x50, (byte)0x4B, (byte)0x03, (byte)0x04}  // ZIP/JAR
    );

    private static final Tika tika = new Tika();

    /**
     * 检查文件安全性
     */
    public static String checkFileSecurity(MultipartFile file) {
        try {
            // 1. 检查扩展名
            String originalFilename = file.getOriginalFilename();
            if (originalFilename != null) {
                String extension = FileUtils.getFileExtension(originalFilename).toLowerCase();
                if (DANGEROUS_EXTENSIONS.contains(extension)) {
                    return "禁止上传 " + extension + " 类型的文件";
                }
            }

            // 2. 检查MIME类型
            String mimeType = file.getContentType();
            if (mimeType != null && DANGEROUS_MIME_TYPES.contains(mimeType.toLowerCase())) {
                return "禁止上传 " + mimeType + " 类型的文件";
            }

            // 3. 检查文件大小（如果需要的话）
            if (file.getSize() <= 0) {
                return "文件大小无效";
            }

            return null; // 安全检查通过

        } catch (Exception e) {
            log.error("文件安全检查异常", e);
            return "文件安全检查失败: " + e.getMessage();
        }
    }

    /**
     * 检查文件内容安全性
     */
    public static boolean isFileContentSafe(File file) {
        try {
            // 1. 使用Tika检测真实文件类型
            String detectedType = tika.detect(file);
            if (detectedType != null) {
                for (String dangerousType : DANGEROUS_MIME_TYPES) {
                    if (detectedType.toLowerCase().contains(dangerousType.toLowerCase())) {
                        log.warn("检测到危险文件类型: {}", detectedType);
                        return false;
                    }
                }
            }

            // 2. 检查文件魔数
            byte[] fileHeader = new byte[4];
            try (var inputStream = Files.newInputStream(file.toPath())) {
                int bytesRead = inputStream.read(fileHeader);
                if (bytesRead >= 2) {
                    for (byte[] magicNumber : DANGEROUS_MAGIC_NUMBERS) {
                        if (startsWith(fileHeader, magicNumber)) {
                            log.warn("检测到危险文件签名");
                            return false;
                        }
                    }
                }
            }

            // 3. 检查文件内容（简单文本检查）
            if (isTextFile(file)) {
                String content = Files.readString(file.toPath()).toLowerCase();
                if (containsDangerousCode(content)) {
                    log.warn("检测到危险代码内容");
                    return false;
                }
            }

            return true;

        } catch (IOException e) {
            log.error("文件内容检查失败", e);
            return false;
        }
    }

    /**
     * 检查文件是否以指定字节数组开头
     */
    private static boolean startsWith(byte[] source, byte[] target) {
        if (source.length < target.length) return false;
        for (int i = 0; i < target.length; i++) {
            if (source[i] != target[i]) return false;
        }
        return true;
    }

    /**
     * 检查是否是文本文件
     */
    private static boolean isTextFile(File file) throws IOException {
        String mimeType = Files.probeContentType(file.toPath());
        return mimeType != null && (
                mimeType.startsWith("text/") ||
                        mimeType.contains("json") ||
                        mimeType.contains("xml")
        );
    }

    /**
     * 检查是否包含危险代码
     */
    private static boolean containsDangerousCode(String content) {
        // 危险代码模式
        String[] dangerousPatterns = {
                "exec(", "system(", "Runtime.getRuntime()",
                "ProcessBuilder", "cmd.exe", "/bin/bash",
                "eval(", "setTimeout(", "setInterval(",
                "document.cookie", "localStorage", "sessionStorage",
                "<script>", "javascript:", "onload=", "onerror="
        };

        for (String pattern : dangerousPatterns) {
            if (content.contains(pattern)) {
                return true;
            }
        }

        return false;
    }
}