package tech.easyflow.ai.node.filegeneration;

import com.openhtmltopdf.outputdevice.helper.ExternalResourceType;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.junit.Assert;
import org.junit.Test;

import java.io.ByteArrayInputStream;

/**
 * 文档渲染器单元测试。
 *
 * @author Codex
 * @since 2026-04-18
 */
public class FileRendererTest {

    @Test
    public void testMarkdownRendererShouldSupportPlainTextAndMarkdown() {
        MarkdownFileRenderer renderer = new MarkdownFileRenderer();
        FileGenerationResult plain = renderer.render(new FileGenerationRequest(
                "hello",
                "plain_text",
                "md",
                "demo.md",
                "default"
        ));
        FileGenerationResult markdown = renderer.render(new FileGenerationRequest(
                "# title",
                "markdown",
                "md",
                "demo.md",
                "default"
        ));

        Assert.assertEquals("hello", new String(plain.getBytes()));
        Assert.assertEquals("# title", new String(markdown.getBytes()));
    }

    @Test
    public void testHtmlRendererShouldSanitizeDangerousHtml() {
        HtmlFileRenderer renderer = new HtmlFileRenderer(new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()));
        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "<script>alert(1)</script><table><tr><td>ok</td></tr></table>",
                "html",
                "html",
                "demo.html",
                "default"
        ));
        String html = new String(result.getBytes());

        Assert.assertTrue(html.contains("<table>"));
        Assert.assertFalse(html.contains("<script>"));
        Assert.assertTrue(html.contains("document-paper"));
    }

    @Test
    public void testHtmlRendererShouldSanitizeMarkdownGeneratedResources() {
        HtmlFileRenderer renderer = new HtmlFileRenderer(new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()));
        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "![remote](https://example.com/demo.png)\n\n# title",
                "markdown",
                "html",
                "demo.html",
                "default"
        ));
        String html = new String(result.getBytes());

        Assert.assertFalse(html.contains("<img"));
        Assert.assertTrue(html.contains("<h1>title</h1>"));
    }

    @Test
    public void testPdfRendererShouldGeneratePdfWithChineseContent() throws Exception {
        PdfFileRenderer renderer = new PdfFileRenderer(new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()));
        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "# 中文标题\n\n- 列表项\n\n| 列1 | 列2 |\n| --- | --- |\n| A | B |",
                "markdown",
                "pdf",
                "demo.pdf",
                "default"
        ));

        byte[] bytes = result.getBytes();
        Assert.assertTrue(bytes.length > 1024);
        Assert.assertEquals("%PDF", new String(bytes, 0, 4));
        try (PDDocument document = PDDocument.load(bytes)) {
            String text = new PDFTextStripper().getText(document);
            Assert.assertTrue(text.contains("中文标题"));
            Assert.assertTrue(text.contains("列表项"));
            Assert.assertTrue(text.contains("列1"));
        }
    }

    @Test
    public void testPdfRendererShouldRenderMarkdownInsteadOfKeepingRawSyntax() throws Exception {
        PdfFileRenderer renderer = new PdfFileRenderer(new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()));
        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "# Java 入门手册\n\n## Hello World\n\n```java\nSystem.out.println(\"你好\");\n```",
                "markdown",
                "pdf",
                "manual.pdf",
                "default"
        ));

        try (PDDocument document = PDDocument.load(result.getBytes())) {
            String text = new PDFTextStripper().getText(document)
                    .replace('\u00A0', ' ')
                    .replace("\r", "");
            Assert.assertTrue(text.contains("Java 入门手册"));
            Assert.assertTrue(text.contains("Hello World"));
            Assert.assertTrue(text.contains("System.out.println"));
            Assert.assertTrue(text.contains("你好"));
            Assert.assertFalse(text.contains("# Java 入门手册"));
            Assert.assertFalse(text.contains("## Hello World"));
            Assert.assertFalse(text.contains("```java"));
        }
    }

    @Test
    public void testPdfRendererShouldBlockExternalResources() {
        PdfFileRenderer renderer = new PdfFileRenderer(new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()) {
            @Override
            public String buildDocument(FileGenerationRequest request) {
                return """
                        <!DOCTYPE html>
                        <html>
                        <body>
                        <p>blocked resource</p>
                        <img src="https://example.com/demo.png" alt="remote" />
                        </body>
                        </html>
                        """;
            }
        });

        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "ignored",
                "html",
                "pdf",
                "demo.pdf",
                "default"
        ));

        Assert.assertTrue(result.getBytes().length > 512);
        Assert.assertEquals("%PDF", new String(result.getBytes(), 0, 4));
        Assert.assertNull(PdfFileRenderer.resolveBlockedUri("https://base.example", "https://example.com/demo.png"));
        Assert.assertFalse(PdfFileRenderer.denyExternalResource("https://example.com/demo.png", ExternalResourceType.IMAGE_RASTER));
    }

    @Test
    public void testPdfRendererShouldFailFastWhenFontMissing() {
        IllegalStateException exception = Assert.assertThrows(
                IllegalStateException.class,
                () -> new PdfFileRenderer(
                        new HtmlDocumentBuilder(new MarkdownSupport(), new HtmlSanitizer()),
                        "fonts/missing-font.otf"
                )
        );

        Assert.assertTrue(exception.getMessage().contains("字体资源缺失"));
    }

    @Test
    public void testDocxRendererShouldRenderMarkdownBlocks() throws Exception {
        DocxFileRenderer renderer = new DocxFileRenderer(new MarkdownSupport());
        FileGenerationResult result = renderer.render(new FileGenerationRequest(
                "# 标题\n\n正文 **加粗** 与 [链接](https://example.com)\n\n- 列表项\n\n> 引用内容\n\n| 列1 | 列2 |\n| --- | --- |\n| A | B |",
                "markdown",
                "docx",
                "demo.docx",
                "default"
        ));

        try (XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(result.getBytes()))) {
            Assert.assertTrue(document.getParagraphs().stream().anyMatch(item -> item.getText().contains("标题")));
            Assert.assertTrue(document.getParagraphs().stream().anyMatch(item -> item.getText().contains("列表项")));
            Assert.assertTrue(document.getParagraphs().stream().anyMatch(item -> item.getText().contains("引用内容")));
            Assert.assertTrue(document.getParagraphs().stream().anyMatch(item -> item.getText().contains("链接")));
            Assert.assertTrue(document.getHyperlinks().length > 0);
            Assert.assertEquals("https://example.com", document.getHyperlinks()[0].getURL());
            Assert.assertEquals(1, document.getTables().size());
            Assert.assertEquals("列1", document.getTables().get(0).getRow(0).getCell(0).getText());
            Assert.assertEquals("A", document.getTables().get(0).getRow(1).getCell(0).getText());
        }
    }
}
