import React from "react";
import { describe, it, expect, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { WikiLinkRenderer } from "../WikiLinkRenderer";

// Mock Next.js Link component
vi.mock("next/link", () => ({
  default: ({ children, href }: { children: React.ReactNode; href: string }): React.JSX.Element => {
    return <a href={href}>{children}</a>;
  },
}));

describe("WikiLinkRenderer", (): void => {
  it("renders plain HTML without wiki-links", (): void => {
    const html = "<p>This is plain <strong>HTML</strong> content.</p>";
    render(<WikiLinkRenderer html={html} />);

    expect(screen.getByText(/This is plain/)).toBeInTheDocument();
    expect(screen.getByText("HTML")).toBeInTheDocument();
  });

  it("converts basic wiki-links [[slug]] to anchor tags", (): void => {
    const html = "<p>Check out [[my-entry]] for more info.</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const link = container.querySelector('a[data-wiki-link="true"]');
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute("href", "/knowledge/my-entry");
    expect(link).toHaveAttribute("data-slug", "my-entry");
    expect(link).toHaveTextContent("my-entry");
  });

  it("converts wiki-links with display text [[slug|text]]", (): void => {
    const html = "<p>Read the [[architecture|Architecture Guide]] please.</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const link = container.querySelector('a[data-wiki-link="true"]');
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute("href", "/knowledge/architecture");
    expect(link).toHaveAttribute("data-slug", "architecture");
    expect(link).toHaveTextContent("Architecture Guide");
  });

  it("handles multiple wiki-links in the same content", (): void => {
    const html = "<p>See [[page-one]] and [[page-two|Page Two]] for details.</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const links = container.querySelectorAll('a[data-wiki-link="true"]');
    expect(links.length).toBe(2);

    expect(links[0]).toHaveAttribute("href", "/knowledge/page-one");
    expect(links[0]).toHaveTextContent("page-one");

    expect(links[1]).toHaveAttribute("href", "/knowledge/page-two");
    expect(links[1]).toHaveTextContent("Page Two");
  });

  it("handles wiki-links with whitespace", (): void => {
    const html = "<p>Check [[ my-entry ]] and [[ other-entry | Other Entry ]]</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const links = container.querySelectorAll('a[data-wiki-link="true"]');
    expect(links.length).toBe(2);

    // Whitespace should be trimmed
    expect(links[0]).toHaveAttribute("href", "/knowledge/my-entry");
    expect(links[1]).toHaveAttribute("href", "/knowledge/other-entry");
    expect(links[1]).toHaveTextContent("Other Entry");
  });

  it("escapes HTML in link text to prevent XSS", (): void => {
    // SEC-WEB-2: DOMPurify now sanitizes entire HTML BEFORE wiki-link processing
    // Script tags are stripped, which may break wiki-link patterns like [[entry|]]
    const html = "<p>[[entry|<script>alert('xss')</script>]]</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    // After sanitization: <p>[[entry|]]</p> - malformed wiki-link (empty display text with |)
    // The wiki-link regex doesn't match [[entry|]] because |([^\]]+) requires 1+ chars
    // So no wiki-link is created - the XSS is prevented by stripping dangerous content

    // No script tags in output
    expect(container.innerHTML).not.toContain("<script>");
    expect(container.innerHTML).not.toContain("alert");
    expect(container.innerHTML).not.toContain("xss");
  });

  it("preserves other HTML structure while converting wiki-links", (): void => {
    const html = `
      <h2>Title</h2>
      <p>Paragraph with [[link-one|Link One]].</p>
      <ul>
        <li>Item with [[link-two]]</li>
      </ul>
    `;
    const { container } = render(<WikiLinkRenderer html={html} />);

    // Should preserve HTML structure
    expect(container.querySelector("h2")).toBeInTheDocument();
    expect(container.querySelector("ul")).toBeInTheDocument();
    expect(container.querySelector("li")).toBeInTheDocument();

    // Should have converted wiki-links
    const links = container.querySelectorAll('a[data-wiki-link="true"]');
    expect(links.length).toBe(2);
  });

  it("applies custom className to wrapper div", (): void => {
    const html = "<p>Content</p>";
    const { container } = render(<WikiLinkRenderer html={html} className="custom-class" />);

    const wrapper = container.querySelector(".wiki-link-content");
    expect(wrapper).toHaveClass("custom-class");
  });

  it("applies wiki-link styling classes", (): void => {
    const html = "<p>[[test-link]]</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const link = container.querySelector('a[data-wiki-link="true"]');
    expect(link).toHaveClass("wiki-link");
    expect(link).toHaveClass("text-blue-600");
    expect(link).toHaveClass("dark:text-blue-400");
    expect(link).toHaveClass("underline");
  });

  it("handles encoded special characters in slugs", (): void => {
    const html = "<p>[[hello-world-2026]]</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    const link = container.querySelector('a[data-wiki-link="true"]');
    expect(link).toHaveAttribute("href", "/knowledge/hello-world-2026");
  });

  it("does not convert malformed wiki-links", (): void => {
    const html = "<p>[[incomplete and [mismatched] brackets</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    // Should not create wiki-links for malformed patterns
    const links = container.querySelectorAll('a[data-wiki-link="true"]');
    expect(links.length).toBe(0);

    // Original text should be preserved
    expect(container.textContent).toContain("[[incomplete");
  });

  it("handles nested HTML within paragraphs containing wiki-links", (): void => {
    const html = "<p>Text with <em>emphasis</em> and [[my-link|My Link]].</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    // Should preserve nested HTML
    expect(container.querySelector("em")).toBeInTheDocument();

    // Should still convert wiki-link
    const link = container.querySelector('a[data-wiki-link="true"]');
    expect(link).toHaveAttribute("href", "/knowledge/my-link");
  });

  it("handles empty wiki-links gracefully", (): void => {
    const html = "<p>Empty link: [[]]</p>";
    const { container } = render(<WikiLinkRenderer html={html} />);

    // Should handle empty slugs (though they're not valid)
    // The regex should match but create a link with empty slug
    // Depending on implementation, this might create a link or skip it
    // Either way, it shouldn't crash
    expect(container.textContent).toContain("Empty link:");
  });

  it("memoizes processed HTML to avoid unnecessary re-parsing", (): void => {
    const html = "<p>[[test-link]]</p>";
    const { rerender, container } = render(<WikiLinkRenderer html={html} />);

    const firstLink = container.querySelector('a[data-wiki-link="true"]');
    expect(firstLink).toBeInTheDocument();

    // Re-render with same HTML
    rerender(<WikiLinkRenderer html={html} />);

    // Should still have the link (memoization test is implicit)
    const secondLink = container.querySelector('a[data-wiki-link="true"]');
    expect(secondLink).toBeInTheDocument();
  });

  describe("XSS Attack Vectors", (): void => {
    it("blocks javascript: URLs in slug", (): void => {
      const html = "<p>[[javascript:alert(1)|Click here]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();

      // Original text should be preserved but escaped (prevents execution)
      expect(container.textContent).toContain("[[javascript:alert(1)|Click here]]");
    });

    it("blocks data: URLs in slug", (): void => {
      const html = "<p>[[data:text/html,<script>alert(1)</script>|Link]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();

      // Original text should be preserved (prevents execution)
      expect(container.textContent).toContain("[[data:text/html");
    });

    it("blocks vbscript: URLs in slug", (): void => {
      const html = "<p>[[vbscript:alert(1)|text]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();

      // Original text should be preserved (prevents execution)
      expect(container.textContent).toContain("[[vbscript:alert(1)|text]]");
    });

    it("escapes event handlers in display text", (): void => {
      // SEC-WEB-2: DOMPurify now sanitizes entire HTML BEFORE wiki-link processing
      // After sanitization: <p>[[valid-link|]]</p> - malformed wiki-link (empty display text)
      const html = "<p>[[valid-link|<img src=x onerror=alert(1)>]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // XSS payload is stripped - that's the main security goal
      expect(container.innerHTML).not.toContain("onerror");
      expect(container.innerHTML).not.toContain("alert(1)");
      expect(container.innerHTML).not.toContain("<img");
    });

    it("escapes iframe injection in display text", (): void => {
      // SEC-WEB-2: DOMPurify now sanitizes entire HTML BEFORE wiki-link processing
      // After sanitization: <p>[[valid-link|]]</p> - malformed wiki-link (empty display text)
      const html = "<p>[[valid-link|<iframe src=evil.com>]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // XSS payload is stripped - that's the main security goal
      expect(container.innerHTML).not.toContain("<iframe");
      expect(container.innerHTML).not.toContain("evil.com");
    });

    it("blocks script tags in slug", (): void => {
      const html = "<p>[[<script>alert(1)</script>|text]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();

      // Should escape the original text
      expect(container.innerHTML).not.toContain("<script>");
    });

    it("escapes onclick handlers in display text", (): void => {
      const html = "<p>[[valid-link|<div onclick=alert(1)>Click</div>]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();

      // DOMPurify removes HTML but keeps text content
      const linkHtml = link?.innerHTML ?? "";
      expect(linkHtml).not.toContain("onclick");
      expect(linkHtml).not.toContain("<div");
      expect(linkHtml).toContain("Click"); // Text content preserved
    });

    it("blocks HTML entity bypass in slug", (): void => {
      const html = "<p>[[&lt;script&gt;alert(1)&lt;/script&gt;|text]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug with entities)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();
    });

    it("blocks URL-encoded javascript protocol", (): void => {
      const html = "<p>[[%6A%61%76%61%73%63%72%69%70%74%3Aalert(1)|text]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Should not create a link (invalid slug)
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).not.toBeInTheDocument();
    });

    it("escapes SVG with embedded scripts in display text", (): void => {
      // SEC-WEB-2: DOMPurify now sanitizes entire HTML BEFORE wiki-link processing
      // After sanitization: <p>[[valid-link|]]</p> - malformed wiki-link (empty display text)
      const html = "<p>[[valid-link|<svg><script>alert(1)</script></svg>]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // XSS payload is stripped - that's the main security goal
      expect(container.innerHTML).not.toContain("<svg>");
      expect(container.innerHTML).not.toContain("<script>");
      expect(container.innerHTML).not.toContain("alert");
    });

    it("blocks object/embed tags in display text", (): void => {
      // SEC-WEB-2: DOMPurify now sanitizes entire HTML BEFORE wiki-link processing
      // After sanitization: <p>[[valid-link|]]</p> - malformed wiki-link (empty display text)
      const html = "<p>[[valid-link|<object data=evil.com></object>]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // XSS payload is stripped - that's the main security goal
      expect(container.innerHTML).not.toContain("<object");
      expect(container.innerHTML).not.toContain("evil.com");
    });
  });

  describe("SEC-WEB-2: Stored XSS via surrounding HTML content", (): void => {
    it("sanitizes script tags in surrounding HTML before wiki-link processing", (): void => {
      const html = "<p>Safe text</p><script>alert('xss')</script><p>[[my-link]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Script tag should be removed
      expect(container.innerHTML).not.toContain("<script>");
      expect(container.innerHTML).not.toContain("alert('xss')");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
      expect(link).toHaveAttribute("href", "/knowledge/my-link");

      // Safe content preserved
      expect(container.textContent).toContain("Safe text");
    });

    it("sanitizes img tags with onerror handlers in surrounding HTML", (): void => {
      const html = '<p>Content with [[link]]</p><img src="x" onerror="alert(1)">';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Image tag should be removed (not in allowed tags)
      expect(container.innerHTML).not.toContain("<img");
      expect(container.innerHTML).not.toContain("onerror");
      expect(container.innerHTML).not.toContain("alert(1)");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes iframe injection in surrounding HTML", (): void => {
      const html = '<iframe src="https://evil.com"></iframe><p>[[safe-link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Iframe should be removed
      expect(container.innerHTML).not.toContain("<iframe");
      expect(container.innerHTML).not.toContain("evil.com");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes SVG with embedded scripts in surrounding HTML", (): void => {
      const html = '<svg onload="alert(1)"><script>evil()</script></svg><p>[[my-entry]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // SVG and script should be removed
      expect(container.innerHTML).not.toContain("<svg");
      expect(container.innerHTML).not.toContain("<script>");
      expect(container.innerHTML).not.toContain("onload");
      expect(container.innerHTML).not.toContain("evil()");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes event handlers on allowed tags in surrounding HTML", (): void => {
      const html = '<div onclick="alert(1)">Click me</div><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // onclick should be removed but div preserved
      expect(container.innerHTML).not.toContain("onclick");
      expect(container.innerHTML).not.toContain("alert(1)");
      expect(container.textContent).toContain("Click me");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes anchor tags with javascript: protocol in surrounding HTML", (): void => {
      const html = '<a href="javascript:alert(1)">Evil link</a><p>[[safe-link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Anchor tags not in allowed list should be removed
      expect(container.innerHTML).not.toContain("javascript:");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes form injection in surrounding HTML", (): void => {
      const html = '<form action="https://evil.com"><input type="text"></form><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Form elements should be removed
      expect(container.innerHTML).not.toContain("<form");
      expect(container.innerHTML).not.toContain("<input");
      expect(container.innerHTML).not.toContain("evil.com");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes object/embed tags in surrounding HTML", (): void => {
      const html = '<object data="https://evil.com/flash.swf"></object><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Object should be removed
      expect(container.innerHTML).not.toContain("<object");
      expect(container.innerHTML).not.toContain("evil.com");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes style tags with malicious CSS in surrounding HTML", (): void => {
      const html = '<style>body { background: url("javascript:alert(1)") }</style><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Style tag should be removed
      expect(container.innerHTML).not.toContain("<style");
      expect(container.innerHTML).not.toContain("javascript:");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("preserves safe formatting tags while removing dangerous ones", (): void => {
      const html =
        "<p><strong>Bold</strong> and <em>italic</em></p><script>evil()</script><p>[[my-link|My Link]]</p>";
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Safe tags preserved
      expect(container.querySelector("strong")).toBeInTheDocument();
      expect(container.querySelector("em")).toBeInTheDocument();
      expect(container.textContent).toContain("Bold");
      expect(container.textContent).toContain("italic");

      // Script removed
      expect(container.innerHTML).not.toContain("<script>");

      // Wiki-link works
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
      expect(link).toHaveTextContent("My Link");
    });

    it("sanitizes base64-encoded data URIs in img tags", (): void => {
      const html =
        '<img src="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Image with data URI should be removed
      expect(container.innerHTML).not.toContain("<img");
      expect(container.innerHTML).not.toContain("data:");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("sanitizes meta refresh in surrounding HTML", (): void => {
      const html = '<meta http-equiv="refresh" content="0;url=https://evil.com"><p>[[link]]</p>';
      const { container } = render(<WikiLinkRenderer html={html} />);

      // Meta tag should be removed
      expect(container.innerHTML).not.toContain("<meta");
      expect(container.innerHTML).not.toContain("evil.com");

      // Wiki-link should still work
      const link = container.querySelector('a[data-wiki-link="true"]');
      expect(link).toBeInTheDocument();
    });

    it("handles complex mixed content with multiple attack vectors", (): void => {
      const html = `
        <p>Normal paragraph with [[good-link|Good Link]]</p>
        <script>stealCookies()</script>
        <img src="x" onerror="alert(1)">
        <iframe src="evil.com"></iframe>
        <p>Another paragraph</p>
        <svg onload="evil()"></svg>
        <p>Final text with [[another-link]]</p>
      `;
      const { container } = render(<WikiLinkRenderer html={html} />);

      // All dangerous content removed
      expect(container.innerHTML).not.toContain("<script>");
      expect(container.innerHTML).not.toContain("<img");
      expect(container.innerHTML).not.toContain("<iframe");
      expect(container.innerHTML).not.toContain("<svg");
      expect(container.innerHTML).not.toContain("stealCookies");
      expect(container.innerHTML).not.toContain("onerror");
      expect(container.innerHTML).not.toContain("onload");

      // Safe content preserved
      expect(container.textContent).toContain("Normal paragraph");
      expect(container.textContent).toContain("Another paragraph");
      expect(container.textContent).toContain("Final text");

      // Both wiki-links work
      const links = container.querySelectorAll('a[data-wiki-link="true"]');
      expect(links.length).toBe(2);
      expect(links[0]).toHaveTextContent("Good Link");
      expect(links[1]).toHaveAttribute("href", "/knowledge/another-link");
    });
  });
});
