import { describe, expect, test } from "bun:test"
import { SddMarkdownParser } from "@/tool/document-validation/markdown-parser"

describe("SddMarkdownParser", () => {
  describe("parsing", () => {
    test("parses top-level sections", () => {
      const md = `# Title\n\ncontent\n\n## Section A\n\na content\n\n## Section B\n\nb content`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections.length).toBe(1)
      expect(sections[0].level).toBe(1)
      expect(sections[0].title).toBe("Title")
      expect(sections[0].children.length).toBe(2)
      expect(sections[0].children[0].title).toBe("Section A")
      expect(sections[0].children[1].title).toBe("Section B")
    })

    test("ignores headers inside code fences", () => {
      const md = `# Title\n\n\`\`\`markdown\n# Inside code\n\`\`\`\n\n## Real Section\n\nreal content`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].children.length).toBe(1)
      expect(sections[0].children[0].title).toBe("Real Section")
    })

    test("handles nested sections", () => {
      const md = `# H1\n\n## H2\n\n### H3\n\n#### H4\n\ncontent`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      const h1 = sections[0]
      expect(h1.level).toBe(1)
      expect(h1.children.length).toBe(1)
      expect(h1.children[0].level).toBe(2)
      expect(h1.children[0].children.length).toBe(1)
      expect(h1.children[0].children[0].level).toBe(3)
      expect(h1.children[0].children[0].children.length).toBe(1)
      expect(h1.children[0].children[0].children[0].level).toBe(4)
    })

    test("extracts content until next same-level header", () => {
      const md = `## A\n\nline1\nline2\n\n## B\n\nline3`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].content).toBe("line1\nline2")
      expect(sections[1].content).toBe("line3")
    })
  })

  describe("searching", () => {
    test("findSection finds nested section", () => {
      const md = `# Root\n\n## Deep\n\nfound me`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()
      const found = parser.findSection(sections, (s) => s.title === "Deep")

      expect(found).toBeDefined()
      expect(found?.title).toBe("Deep")
    })

    test("findSections returns all matching sections", () => {
      const md = `# Root\n\n## Match\n\n### Match\n\n## Other`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()
      const matches = parser.findSections(sections, (s) => s.title === "Match")

      expect(matches.length).toBe(2)
    })
  })

  describe("edge cases", () => {
    test("handles CRLF line endings", () => {
      const md = "# Title\r\n\r\n## Section\r\n\r\ncontent"
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].title).toBe("Title")
      expect(sections[0].children[0].title).toBe("Section")
    })

    test("normalizes titles", () => {
      const md = "# **Bold Title**\n\n## Title with fullwidth spaces"
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].normalizedTitle).toBe("Bold Title")
      expect(sections[0].children[0].normalizedTitle).toBe("Title with fullwidth spaces")
    })

    test("handles empty document", () => {
      const parser = new SddMarkdownParser("")
      const sections = parser.parseSections()
      expect(sections.length).toBe(0)
    })

    test("handles document with only newlines", () => {
      const parser = new SddMarkdownParser("\n\n\n")
      const sections = parser.parseSections()
      expect(sections.length).toBe(0)
    })

    test("handles ~~~ code fences", () => {
      const md = `# Title\n\n~~~js\n# Inside code\n~~~\n\n## Real Section\n\nreal content`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].children.length).toBe(1)
      expect(sections[0].children[0].title).toBe("Real Section")
    })

    test("ignores headers in unclosed code fences", () => {
      const md = `# Title\n\n\`\`\`\n# Inside code\n\n## Also inside\n\nno closing fence`
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].children.length).toBe(0)
    })

    test("findSection returns undefined for empty array", () => {
      const parser = new SddMarkdownParser("")
      const found = parser.findSection([], () => true)
      expect(found).toBeUndefined()
    })

    test("findSections returns empty array for empty input", () => {
      const parser = new SddMarkdownParser("")
      const matches = parser.findSections([], () => true)
      expect(matches.length).toBe(0)
    })

    test("handles headers with trailing hashes", () => {
      const md = "# Title ##\n\ncontent\n\n## Section ###\n\nsection content"
      const parser = new SddMarkdownParser(md)
      const sections = parser.parseSections()

      expect(sections[0].title).toBe("Title ##")
      expect(sections[0].children[0].title).toBe("Section ###")
    })
  })
})