import { fireEvent, render, screen } from "@testing-library/react";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { BlockQuote, CodeBlock, Heading, Link } from "../src/markdown";

describe("Heading", () => {
  test("renders h1 with correct level", () => {
    render(<Heading level={1}>Heading 1</Heading>);
    const heading = screen.getByText("Heading 1");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-2xl", "font-bold");
  });

  test("renders h2 with correct level", () => {
    render(<Heading level={2}>Heading 2</Heading>);
    const heading = screen.getByText("Heading 2");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-xl", "font-bold");
  });

  test("renders h3 with correct level", () => {
    render(<Heading level={3}>Heading 3</Heading>);
    const heading = screen.getByText("Heading 3");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-lg", "font-bold");
  });

  test("renders h4 with correct level", () => {
    render(<Heading level={4}>Heading 4</Heading>);
    const heading = screen.getByText("Heading 4");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-md", "font-bold");
  });

  test("renders h5 with correct level", () => {
    render(<Heading level={5}>Heading 5</Heading>);
    const heading = screen.getByText("Heading 5");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-base", "font-bold");
  });

  test("renders h6 with correct level", () => {
    render(<Heading level={6}>Heading 6</Heading>);
    const heading = screen.getByText("Heading 6");
    expect(heading.tagName).toBe("H1");
    expect(heading).toHaveClass("text-base", "font-bold");
  });
});

describe("BlockQuote", () => {
  test("renders as blockquote element", () => {
    render(<BlockQuote>Quote content</BlockQuote>);
    const quote = screen.getByText("Quote content");
    expect(quote.tagName).toBe("BLOCKQUOTE");
  });

  test("has left border style", () => {
    render(<BlockQuote>Styled quote</BlockQuote>);
    const quote = screen.getByText("Styled quote");
    expect(quote).toHaveClass(
      "border-l-4",
      "border-gray-300",
      "pl-4",
      "italic",
    );
  });

  test("renders with custom className", () => {
    render(<BlockQuote className="custom-class">Custom</BlockQuote>);
    const quote = screen.getByText("Custom");
    expect(quote).toHaveClass("custom-class");
  });
});

describe("Link", () => {
  beforeEach(() => {
    vi.spyOn(navigator.clipboard, "writeText").mockImplementation(vi.fn());
  });

  test("renders as anchor element", () => {
    render(<Link href="https://example.com">Link Text</Link>);
    const link = screen.getByText("Link Text");
    expect(link.tagName).toBe("A");
    expect(link).toHaveAttribute("href", "https://example.com");
  });

  test("opens in new tab by default", () => {
    render(<Link href="https://example.com">New Tab</Link>);
    const link = screen.getByText("New Tab");
    expect(link).toHaveAttribute("target", "_blank");
  });

  test("uses provided target when specified", () => {
    render(
      <Link href="https://example.com" target="_self">
        Same Tab
      </Link>,
    );
    const link = screen.getByText("Same Tab");
    expect(link).toHaveAttribute("target", "_self");
  });

  test("adds safe rel attributes for external links", () => {
    render(<Link href="https://example.com">Safe Link</Link>);
    const link = screen.getByText("Safe Link");
    expect(link).toHaveAttribute("rel", "noopener noreferrer");
  });

  test("preserves existing rel attributes", () => {
    render(
      <Link href="https://example.com" rel="custom-rel">
        Custom Rel
      </Link>,
    );
    const link = screen.getByText("Custom Rel");
    expect(link).toHaveAttribute("rel", "noopener noreferrer custom-rel");
  });

  test("uses provided rel for non-external links", () => {
    render(
      <Link href="/local" target="_self" rel="custom">
        Local Link
      </Link>,
    );
    const link = screen.getByText("Local Link");
    expect(link).toHaveAttribute("rel", "custom");
  });

  test("blocks javascript: href", () => {
    render(<Link href="javascript:alert(1)">JS Link</Link>);
    const link = screen.getByText("JS Link");
    expect(link).not.toHaveAttribute("href");
  });

  test("blocks data: href", () => {
    render(
      <Link href="data:text/html,<script>alert(1)</script>">Data Link</Link>,
    );
    const link = screen.getByText("Data Link");
    expect(link).not.toHaveAttribute("href");
  });

  test("blocks vbscript: href", () => {
    render(<Link href="vbscript:alert(1)">VB Link</Link>);
    const link = screen.getByText("VB Link");
    expect(link).not.toHaveAttribute("href");
  });

  test("has hover underline style", () => {
    render(<Link href="https://example.com">Underline Link</Link>);
    const link = screen.getByText("Underline Link");
    expect(link).toHaveClass("hover:underline", "underline-offset-1");
  });
});

describe("CodeBlock", () => {
  beforeEach(() => {
    Object.defineProperty(navigator, "clipboard", {
      value: { writeText: vi.fn() },
      configurable: true,
    });
  });

  test("renders code without language as inline code", () => {
    render(<CodeBlock>inline code</CodeBlock>);
    const code = screen.getByText("inline code");
    expect(code.tagName).toBe("CODE");
    expect(code).toHaveClass(
      "rounded-md",
      "px-1",
      "py-0.5",
      "text-[85%]",
      "bg-gray-100",
      "dark:bg-gray-800",
    );
  });

  test("renders code with language as syntax highlighted block", () => {
    render(
      <CodeBlock className="language-javascript">
        console.log("hello")
      </CodeBlock>,
    );
    const codeContainer = document.querySelector(".overflow-x-auto");
    expect(codeContainer).toBeInTheDocument();
  });

  test("shows language label for code block", () => {
    render(<CodeBlock className="language-python">print('hello')</CodeBlock>);
    expect(screen.getByText("python")).toBeInTheDocument();
  });

  test("has copy button in code block", () => {
    render(<CodeBlock className="language-js">const x = 1;</CodeBlock>);
    const copyButton = screen.getByRole("button", { name: "Copy" });
    expect(copyButton).toBeInTheDocument();
  });

  test("copy button copies code content", () => {
    render(<CodeBlock className="language-js">const x = 1;</CodeBlock>);
    const copyButton = screen.getByRole("button", { name: "Copy" });
    fireEvent.click(copyButton);

    expect(navigator.clipboard.writeText).toHaveBeenCalledWith("const x = 1;");
  });

  test("copy button shows Copied after clicking", () => {
    render(<CodeBlock className="language-js">const x = 1;</CodeBlock>);
    const copyButton = screen.getByRole("button", { name: "Copy" });
    fireEvent.click(copyButton);

    expect(screen.getByRole("button", { name: "Copied" })).toBeInTheDocument();
  });
});