import { describe, it, expect } from "vitest";
import { renderMarkdown, renderMarkdownSync, markdownToPlainText } from "./markdown";

describe("Markdown Rendering", () => {
  describe("renderMarkdown", () => {
    it("should render basic markdown to HTML", async () => {
      const markdown = "# Hello World\n\nThis is **bold** text.";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<h1");
      expect(html).toContain("Hello World");
      expect(html).toContain("<strong>bold</strong>");
    });

    it("should handle empty input", async () => {
      const html = await renderMarkdown("");
      expect(html).toBe("");
    });

    it("should handle null/undefined input", async () => {
      expect(await renderMarkdown(null as any)).toBe("");
      expect(await renderMarkdown(undefined as any)).toBe("");
    });

    it("should sanitize potentially dangerous HTML", async () => {
      const markdown = '<script>alert("XSS")</script>\n\n**Safe** content';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("<script>");
      expect(html).not.toContain("alert");
      expect(html).toContain("<strong>Safe</strong>");
    });

    it("should sanitize onclick handlers", async () => {
      const markdown = '[Click me](javascript:alert("XSS"))';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("javascript:");
      expect(html).not.toContain("onclick");
    });
  });

  describe("GFM Features", () => {
    it("should render tables", async () => {
      const markdown = `
| Header 1 | Header 2 |
|----------|----------|
| Cell 1   | Cell 2   |
| Cell 3   | Cell 4   |
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<table>");
      expect(html).toContain("<thead>");
      expect(html).toContain("<tbody>");
      expect(html).toContain("<th>Header 1</th>");
      expect(html).toContain("<td>Cell 1</td>");
    });

    it("should render strikethrough text", async () => {
      const markdown = "This is ~~deleted~~ text";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<del>deleted</del>");
    });

    it("should render task lists", async () => {
      const markdown = `
- [ ] Unchecked task
- [x] Checked task
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<input");
      expect(html).toContain('type="checkbox"');
      expect(html).toContain('disabled="disabled"'); // Should be disabled for safety
    });

    it("should render autolinks", async () => {
      const markdown = "Visit https://example.com for more info";
      const html = await renderMarkdown(markdown);

      expect(html).toContain('<a href="https://example.com"');
    });
  });

  describe("Code Highlighting", () => {
    it("should render inline code", async () => {
      const markdown = "Use the `console.log()` function";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<code>");
      expect(html).toContain("console.log()");
    });

    it("should render code blocks with language", async () => {
      const markdown = `
\`\`\`typescript
const greeting: string = "Hello";
console.log(greeting);
\`\`\`
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<pre>");
      expect(html).toContain("<code");
      expect(html).toContain("language-typescript");
      // Should have syntax highlighting
      expect(html.length).toBeGreaterThan(markdown.length);
    });

    it("should render code blocks without language", async () => {
      const markdown = `
\`\`\`
plain text code
\`\`\`
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<pre>");
      expect(html).toContain("<code");
      expect(html).toContain("plain text code");
    });
  });

  describe("Links and Images", () => {
    it("should render links with target=_blank for external URLs", async () => {
      const markdown = "[External Link](https://example.com)";
      const html = await renderMarkdown(markdown);

      expect(html).toContain('href="https://example.com"');
      expect(html).toContain('target="_blank"');
      expect(html).toContain('rel="noopener noreferrer"');
    });

    it("should render images", async () => {
      const markdown = "![Alt text](https://example.com/image.png)";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<img");
      expect(html).toContain('src="https://example.com/image.png"');
      expect(html).toContain('alt="Alt text"');
    });

    it("should block data URIs for images", async () => {
      const markdown =
        "![Image](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==)";
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("data:");
    });
  });

  describe("Headers and IDs", () => {
    it("should generate IDs for headers", async () => {
      const markdown = "# My Header Title";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<h1");
      expect(html).toContain('id="');
    });

    it("should render all header levels", async () => {
      const markdown = `
# H1
## H2
### H3
#### H4
##### H5
###### H6
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<h1");
      expect(html).toContain("<h2");
      expect(html).toContain("<h3");
      expect(html).toContain("<h4");
      expect(html).toContain("<h5");
      expect(html).toContain("<h6");
    });
  });

  describe("Lists", () => {
    it("should render unordered lists", async () => {
      const markdown = `
- Item 1
- Item 2
  - Nested item
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<ul>");
      expect(html).toContain("<li>Item 1</li>");
      expect(html).toContain("Nested item");
    });

    it("should render ordered lists", async () => {
      const markdown = `
1. First
2. Second
3. Third
      `.trim();

      const html = await renderMarkdown(markdown);

      expect(html).toContain("<ol>");
      expect(html).toContain("<li>First</li>");
      expect(html).toContain("<li>Second</li>");
    });
  });

  describe("Quotes and Formatting", () => {
    it("should render blockquotes", async () => {
      const markdown = "> This is a quote\n> Multi-line quote";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<blockquote>");
      expect(html).toContain("This is a quote");
    });

    it("should render emphasis and strong", async () => {
      const markdown = "*italic* **bold** ***bold italic***";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<em>italic</em>");
      expect(html).toContain("<strong>bold</strong>");
    });

    it("should render horizontal rules", async () => {
      const markdown = "Content\n\n---\n\nMore content";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<hr");
    });
  });

  describe("renderMarkdownSync", () => {
    it("should render markdown synchronously", () => {
      const markdown = "# Sync Test\n\n**Bold** text";
      const html = renderMarkdownSync(markdown);

      expect(html).toContain("<h1");
      expect(html).toContain("Sync Test");
      expect(html).toContain("<strong>Bold</strong>");
    });

    it("should handle empty input synchronously", () => {
      const html = renderMarkdownSync("");
      expect(html).toBe("");
    });

    it("should sanitize XSS synchronously", () => {
      const markdown = '<script>alert("XSS")</script>';
      const html = renderMarkdownSync(markdown);

      expect(html).not.toContain("<script>");
      expect(html).not.toContain("alert");
    });
  });

  describe("markdownToPlainText", () => {
    it("should extract plain text from markdown", async () => {
      const markdown = "# Header\n\n**Bold** and *italic* text";
      const plainText = await markdownToPlainText(markdown);

      expect(plainText).toContain("Header");
      expect(plainText).toContain("Bold");
      expect(plainText).toContain("italic");
      expect(plainText).not.toContain("<h1");
      expect(plainText).not.toContain("<strong>");
      expect(plainText).not.toContain("<em>");
    });

    it("should strip all HTML tags", async () => {
      const markdown = "[Link](https://example.com)\n\n![Image](image.png)";
      const plainText = await markdownToPlainText(markdown);

      expect(plainText).not.toContain("<a");
      expect(plainText).not.toContain("<img");
      expect(plainText).toContain("Link");
    });
  });

  describe("Security Tests", () => {
    it("should block iframe injection", async () => {
      const markdown = '<iframe src="https://evil.com"></iframe>';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("<iframe");
    });

    it("should block object/embed tags", async () => {
      const markdown = '<object data="malware.swf"></object>';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("<object");
    });

    it("should block event handlers in markdown links", async () => {
      const markdown = '[Click](# "onclick=alert(1)")';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("onclick");
    });

    it("should sanitize SVG with script", async () => {
      const markdown = '<svg><script>alert("XSS")</script></svg>';
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("<svg");
      expect(html).not.toContain("<script>");
    });

    it("should block data: URI scheme in image src", async () => {
      const markdown = "![XSS](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=)";
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("data:");
      expect(html).not.toContain("text/html");
    });

    it("should block data: URI scheme in links", async () => {
      const markdown = "[Click me](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4=)";
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("data:");
      expect(html).not.toContain("text/html");
    });

    it("should block data: URI with mixed case in images", async () => {
      const markdown =
        "![XSS](Data:image/svg+xml;base64,PHN2Zz48c2NyaXB0PmFsZXJ0KCdYU1MnKTwvc2NyaXB0Pjwvc3ZnPg==)";
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("data:");
      expect(html).not.toContain("Data:");
    });

    it("should block data: URI with leading whitespace", async () => {
      const markdown = "![XSS]( data:image/png;base64,abc123)";
      const html = await renderMarkdown(markdown);

      expect(html).not.toContain("data:");
    });

    it("should block data: URI in sync renderer", () => {
      const markdown = "![XSS](data:image/png;base64,abc123)";
      const html = renderMarkdownSync(markdown);

      expect(html).not.toContain("data:");
    });
  });

  describe("Edge Cases", () => {
    it("should handle very long content", async () => {
      const markdown = "# Test\n\n" + "A ".repeat(10000);
      const html = await renderMarkdown(markdown);

      expect(html.length).toBeGreaterThan(0);
      expect(html).toContain("<h1");
    });

    it("should handle unicode characters", async () => {
      const markdown = "# 你好 World 🌍\n\n**Émojis**: 😀 ✨ 🚀";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("你好");
      expect(html).toContain("🌍");
      expect(html).toContain("😀");
    });

    it("should handle nested markdown correctly", async () => {
      const markdown = "**Bold with _italic_ inside**";
      const html = await renderMarkdown(markdown);

      expect(html).toContain("<strong>");
      expect(html).toContain("<em>");
    });
  });
});
