package com.gzzm.lobster.parse;

import com.gzzm.platform.commons.Tools;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

/**
 * FileMagic —— 按 magic bytes 识别真实文件类型 /
 * Detect real file extension by magic bytes (不信任用户传的 filename 后缀).
 *
 * <p>应对用户把 {@code 报告.pdf} 改名成 {@code 报告.doc} 这种常见情况——
 * 解析端如果按后缀路由就会用错 parser；调用方先跑 {@link #detectExt} 修正扩展名再路由。
 *
 * <p>覆盖：pdf/docx/xlsx/pptx/ofd/doc/xls/ppt。识别不出时返回 null，由调用方退回到原 filename 后缀。
 */
final class FileMagic {

    /** ZIP 内部文件名是 ASCII，用 ISO_8859_1 把字节原样映射，contains 搜子串即可. */
    private static final int ZIP_SCAN_LIMIT = 64 * 1024;

    private FileMagic() {}

    /**
     * 按 magic bytes 判真实类型；返回小写扩展名或 null（未识别）.
     */
    static String detectExt(byte[] bytes) {
        if (bytes == null || bytes.length < 4) return null;

        // PDF: %PDF
        if (bytes[0] == 0x25 && bytes[1] == 0x50 && bytes[2] == 0x44 && bytes[3] == 0x46) {
            return "pdf";
        }

        // OOXML / OFD / 其它 ZIP-based: PK\x03\x04
        if (bytes[0] == 0x50 && bytes[1] == 0x4B && bytes[2] == 0x03 && bytes[3] == 0x04) {
            // 只 decode 前 64KB——xlsx/docx/pptx 的 local file headers 都在文件头部，
            // 全文 decode 对 50MB 文件是 ~50MB 瞬态 String，内存浪费
            int scan = Math.min(bytes.length, ZIP_SCAN_LIMIT);
            String head = new String(bytes, 0, scan, StandardCharsets.ISO_8859_1);
            // OFD 优先判（也是 ZIP 包）
            if (head.contains("OFD.xml") && head.contains("Doc_0/Pages/")) return "ofd";
            if (head.contains("xl/workbook.xml")) return "xlsx";
            if (head.contains("ppt/presentation.xml")) return "pptx";
            if (head.contains("word/document.xml")) return "docx";
            // 未识别的 ZIP —— null，让调用方保留原后缀（可能是 odt/ods/odp 等非 OOXML 包）
            return null;
        }

        // CFB (Compound File Binary)，老 Office 格式 doc/xls/ppt 共用：D0 CF 11 E0
        if ((bytes[0] & 0xFF) == 0xD0 && (bytes[1] & 0xFF) == 0xCF
                && (bytes[2] & 0xFF) == 0x11 && (bytes[3] & 0xFF) == 0xE0) {
            return detectCfb(bytes);
        }

        return null;
    }

    /**
     * 已确认是 CFB 容器后，按 xls → doc → ppt 顺序试探 POI reader；
     * 能 open 的那家就是真身。POI 读失败会抛各种 IO / OLE2 异常——都当作"不是这类"吞掉。
     */
    private static String detectCfb(byte[] bytes) {
        try {
            POIFSFileSystem fs = new POIFSFileSystem(new ByteArrayInputStream(bytes));
            try {
                new HSSFWorkbook(fs);
                return "xls";
            } catch (Throwable ignore) { /* 不是 xls */ }
            try {
                new HWPFDocument(fs);
                return "doc";
            } catch (Throwable ignore) { /* 不是 doc */ }
            try {
                new HSLFSlideShow(fs);
                return "ppt";
            } catch (Throwable ignore) { /* 不是 ppt */ }
        } catch (Throwable t) {
            // POIFS 打开失败——可能是损坏的 CFB，记 warn 让调用方按原后缀兜底
            try { Tools.log("[FileMagic] POIFS open failed", t); } catch (Throwable ignore) { /* ignore */ }
        }
        return null;
    }
}
