import { describe, it, expect } from "vitest";
import { parseWikiLinks, WikiLink } from "./wiki-link-parser";

describe("Wiki Link Parser", () => {
  describe("Basic Parsing", () => {
    it("should parse a simple wiki link", () => {
      const content = "This is a [[Page Name]] in text.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0]).toEqual({
        raw: "[[Page Name]]",
        target: "Page Name",
        displayText: "Page Name",
        start: 10,
        end: 23,
      });
    });

    it("should parse multiple wiki links", () => {
      const content = "Link to [[First Page]] and [[Second Page]].";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(2);
      expect(links[0].target).toBe("First Page");
      expect(links[0].start).toBe(8);
      expect(links[0].end).toBe(22);
      expect(links[1].target).toBe("Second Page");
      expect(links[1].start).toBe(27);
      expect(links[1].end).toBe(42);
    });

    it("should handle empty content", () => {
      const links = parseWikiLinks("");
      expect(links).toEqual([]);
    });

    it("should handle content without links", () => {
      const content = "This is just plain text with no wiki links.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should parse link by slug (kebab-case)", () => {
      const content = "Reference to [[page-slug-name]].";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("page-slug-name");
      expect(links[0].displayText).toBe("page-slug-name");
    });
  });

  describe("Display Text Variation", () => {
    it("should parse link with custom display text", () => {
      const content = "See [[Page Name|custom display]] for details.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0]).toEqual({
        raw: "[[Page Name|custom display]]",
        target: "Page Name",
        displayText: "custom display",
        start: 4,
        end: 32,
      });
    });

    it("should parse multiple links with display text", () => {
      const content = "[[First|One]] and [[Second|Two]]";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(2);
      expect(links[0].target).toBe("First");
      expect(links[0].displayText).toBe("One");
      expect(links[1].target).toBe("Second");
      expect(links[1].displayText).toBe("Two");
    });

    it("should handle display text with special characters", () => {
      const content = "[[Page|Click here! (details)]]";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].displayText).toBe("Click here! (details)");
    });

    it("should handle pipe character in target but default display", () => {
      const content = "[[Page Name]]";
      const links = parseWikiLinks(content);

      expect(links[0].target).toBe("Page Name");
      expect(links[0].displayText).toBe("Page Name");
    });
  });

  describe("Edge Cases - Brackets", () => {
    it("should not parse single brackets", () => {
      const content = "This [is not] a wiki link.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse three or more opening brackets", () => {
      const content = "This [[[is not]]] a wiki link.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse unmatched brackets", () => {
      const content = "This [[is incomplete";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse reversed brackets", () => {
      const content = "This ]]not a link[[ text.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should handle nested brackets inside link text", () => {
      const content = "[[Page [with] brackets]]";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("Page [with] brackets");
    });

    it("should handle nested double brackets", () => {
      // This is tricky - we should parse the outer link
      const content = "[[Outer [[inner]] link]]";
      const links = parseWikiLinks(content);

      // Should not parse nested double brackets - only the first valid one
      expect(links).toHaveLength(1);
      expect(links[0].raw).toBe("[[Outer [[inner]]");
    });
  });

  describe("Edge Cases - Escaped Brackets", () => {
    it("should not parse escaped opening brackets", () => {
      const content = "This \\[[is not a link]] text.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should parse link after escaped brackets", () => {
      const content = "Escaped \\[[not link]] but [[real link]] here.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("real link");
    });

    it("should handle backslash before brackets in various positions", () => {
      const content = "Text \\[[ and [[valid link]] more \\]].";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("valid link");
    });
  });

  describe("Edge Cases - Code Blocks", () => {
    it("should not parse links in inline code", () => {
      const content = "Use `[[WikiLink]]` syntax for linking.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse links in fenced code blocks", () => {
      const content = `
Here is some text.

\`\`\`
[[Link in code block]]
\`\`\`

End of text.
      `;
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse links in indented code blocks", () => {
      const content = `
Normal text here.

    [[Link in indented code]]
    More code here

Normal text again.
      `;
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should parse links outside code blocks but not inside", () => {
      const content = `
[[Valid Link]]

\`\`\`
[[Invalid Link]]
\`\`\`

[[Another Valid Link]]
      `;
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(2);
      expect(links[0].target).toBe("Valid Link");
      expect(links[1].target).toBe("Another Valid Link");
    });

    it("should not parse links in code blocks with language", () => {
      const content = `
\`\`\`typescript
const link = "[[Not A Link]]";
\`\`\`
      `;
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should handle multiple inline code sections", () => {
      const content = "Use `[[link1]]` or `[[link2]]` but [[real link]] works.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("real link");
    });

    it("should handle unclosed code backticks correctly", () => {
      const content = "Start `code [[link1]] still in code [[link2]]";
      const links = parseWikiLinks(content);
      // If backtick is unclosed, we shouldn't parse any links after it
      expect(links).toEqual([]);
    });

    it("should handle adjacent code blocks", () => {
      const content = "`[[code1]]` text [[valid]] `[[code2]]`";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("valid");
    });
  });

  describe("Edge Cases - Empty and Malformed", () => {
    it("should not parse empty link brackets", () => {
      const content = "Empty [[]] brackets.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should not parse whitespace-only links", () => {
      const content = "Whitespace [[   ]] link.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should trim whitespace from link targets", () => {
      const content = "Link [[  Page Name  ]] here.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("Page Name");
      expect(links[0].displayText).toBe("Page Name");
    });

    it("should trim whitespace from display text", () => {
      const content = "Link [[Target|  display text  ]] here.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("Target");
      expect(links[0].displayText).toBe("display text");
    });

    it("should not parse link with empty target but display text", () => {
      const content = "Link [[|display only]] here.";
      const links = parseWikiLinks(content);
      expect(links).toEqual([]);
    });

    it("should handle link with empty display text", () => {
      const content = "Link [[Target|]] here.";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("Target");
      expect(links[0].displayText).toBe("Target");
    });

    it("should handle multiple pipes", () => {
      const content = "Link [[Target|display|extra]] here.";
      const links = parseWikiLinks(content);

      // Should use first pipe as separator
      expect(links).toHaveLength(1);
      expect(links[0].target).toBe("Target");
      expect(links[0].displayText).toBe("display|extra");
    });
  });

  describe("Position Tracking", () => {
    it("should track correct positions for single link", () => {
      const content = "Start [[Link]] end";
      const links = parseWikiLinks(content);

      expect(links[0].start).toBe(6);
      expect(links[0].end).toBe(14);
      expect(content.substring(links[0].start, links[0].end)).toBe("[[Link]]");
    });

    it("should track correct positions for multiple links", () => {
      const content = "[[First]] middle [[Second]] end";
      const links = parseWikiLinks(content);

      expect(links[0].start).toBe(0);
      expect(links[0].end).toBe(9);
      expect(links[1].start).toBe(17);
      expect(links[1].end).toBe(27);

      expect(content.substring(links[0].start, links[0].end)).toBe("[[First]]");
      expect(content.substring(links[1].start, links[1].end)).toBe("[[Second]]");
    });

    it("should track positions with display text", () => {
      const content = "Text [[Target|Display]] more";
      const links = parseWikiLinks(content);

      expect(links[0].start).toBe(5);
      expect(links[0].end).toBe(23);
      expect(content.substring(links[0].start, links[0].end)).toBe("[[Target|Display]]");
    });

    it("should track positions in multiline content", () => {
      const content = `Line 1
Line 2 [[Link]]
Line 3`;
      const links = parseWikiLinks(content);

      expect(links[0].start).toBe(14);
      expect(content.substring(links[0].start, links[0].end)).toBe("[[Link]]");
    });
  });

  describe("Complex Scenarios", () => {
    it("should handle realistic markdown content", () => {
      const content = `
# Knowledge Base

This is a reference to [[Main Page]] and [[Getting Started|start here]].

You can also check [[FAQ]] for common questions.

\`\`\`typescript
// This [[should not parse]]
const link = "[[also not parsed]]";
\`\`\`

But [[this works]] after code block.
      `;

      const links = parseWikiLinks(content);

      expect(links).toHaveLength(4);
      expect(links[0].target).toBe("Main Page");
      expect(links[1].target).toBe("Getting Started");
      expect(links[1].displayText).toBe("start here");
      expect(links[2].target).toBe("FAQ");
      expect(links[3].target).toBe("this works");
    });

    it("should handle links at start and end of content", () => {
      const content = "[[Start]] middle [[End]]";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(2);
      expect(links[0].start).toBe(0);
      expect(links[1].end).toBe(content.length);
    });

    it("should handle consecutive links", () => {
      const content = "[[First]][[Second]][[Third]]";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(3);
      expect(links[0].target).toBe("First");
      expect(links[1].target).toBe("Second");
      expect(links[2].target).toBe("Third");
    });

    it("should handle links with unicode characters", () => {
      const content = "Link to [[日本語]] and [[Émojis 🚀]].";
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(2);
      expect(links[0].target).toBe("日本語");
      expect(links[1].target).toBe("Émojis 🚀");
    });

    it("should handle very long link text", () => {
      const longText = "A".repeat(1000);
      const content = `Start [[${longText}]] end`;
      const links = parseWikiLinks(content);

      expect(links).toHaveLength(1);
      expect(links[0].target).toBe(longText);
    });
  });

  describe("Type Safety", () => {
    it("should return correctly typed WikiLink objects", () => {
      const content = "[[Test Link]]";
      const links: WikiLink[] = parseWikiLinks(content);

      expect(links[0]).toHaveProperty("raw");
      expect(links[0]).toHaveProperty("target");
      expect(links[0]).toHaveProperty("displayText");
      expect(links[0]).toHaveProperty("start");
      expect(links[0]).toHaveProperty("end");

      expect(typeof links[0].raw).toBe("string");
      expect(typeof links[0].target).toBe("string");
      expect(typeof links[0].displayText).toBe("string");
      expect(typeof links[0].start).toBe("number");
      expect(typeof links[0].end).toBe("number");
    });
  });
});
