import { EditorState, TextSelection } from "prosemirror-state"
import { describe, expect, it, vi } from "vitest"
import {
  createPatternPlugin,
  getPatternSession,
  type PatternSession,
} from "@/composables/usePatternPlugin"
import { contentToDoc } from "@/lib/content-model"

describe("Pattern plugin", () => {
  function createTestState(content: string, cursorPos?: number) {
    const doc = contentToDoc(content)
    const pos = cursorPos !== undefined ? cursorPos : doc.content.size - 1
    const plugins = [
      createPatternPlugin({
        onPatternOpen: vi.fn(),
        onPatternUpdate: vi.fn(),
        onPatternClose: vi.fn(),
      }),
    ]
    return EditorState.create({
      doc,
      selection: TextSelection.create(doc, pos),
      plugins,
    })
  }

  function getSession(state: EditorState): PatternSession | null {
    const newState = state.apply(state.tr)
    return getPatternSession(newState)
  }

  it("detects [[ pattern", () => {
    const state = createTestState("[[query")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("[[")
    expect(session?.kind).toBe("reference")
    expect(session?.query).toBe("query")
  })

  it("detects (( pattern", () => {
    const state = createTestState("((query")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("((")
    expect(session?.kind).toBe("embed")
    expect(session?.query).toBe("query")
  })

  it("detects / command pattern", () => {
    const state = createTestState("/cmd")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("/")
    expect(session?.kind).toBe("command")
    expect(session?.query).toBe("cmd")
  })

  it("detects # tag pattern", () => {
    const state = createTestState("#tag")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("#")
    expect(session?.kind).toBe("tag")
    expect(session?.query).toBe("tag")
  })

  it("returns null when no pattern detected", () => {
    const state = createTestState("hello world")
    const session = getSession(state)

    expect(session).toBeNull()
  })

  it("completes [[ pattern", () => {
    const state = createTestState("[[Page]]")
    const session = getSession(state)

    expect(session).toBeNull()
  })

  it("completes (( pattern", () => {
    const state = createTestState("((block))")
    const session = getSession(state)

    expect(session).toBeNull()
  })

  // ── Multi-line patterns (with hard_break) ──────────────────────────
  // Single \n becomes a hard_break within the same paragraph. Patterns
  // are still detected because the inlineToString walker emits "\n" for
  // each hard_break, so the text before cursor is correct.

  it("detects [[ pattern after hard_break", () => {
    const state = createTestState("line1\n[[query")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("[[")
    expect(session?.query).toBeTruthy()
  })

  it("detects (( pattern after hard_break", () => {
    const state = createTestState("prefix\n((block-id")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("((")
    expect(session?.query).toBeTruthy()
  })

  it("detects / command after hard_break", () => {
    const state = createTestState("text\n/cmd")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("/")
    expect(session?.kind).toBe("command")
  })

  it("detects # tag after hard_break", () => {
    const state = createTestState("text\n#tag")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("#")
    expect(session?.kind).toBe("tag")
  })

  it("detects [[ pattern across hard_break within same paragraph", () => {
    const state = createTestState("[[Page\nhello")
    const session = getSession(state)

    // With hard_break preservation, both lines are in one paragraph,
    // so [[ at the start is detected even after a hard_break.
    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("[[")
    expect(session?.kind).toBe("reference")
    expect(session?.query).toBe("Page\nhello")
  })

  // ── Spaces in delimiter pattern queries ──────────────────────────────

  it("detects [[ pattern with spaces in query", () => {
    const state = createTestState("[[page name")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("[[")
    expect(session?.kind).toBe("reference")
    expect(session?.query).toBe("page name")
  })

  it("detects [[ pattern with spaces in second paragraph", () => {
    const state = createTestState("line1\n[[page name")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("[[")
    expect(session?.query).toBeTruthy()
  })

  it("detects (( pattern with special chars in query", () => {
    const state = createTestState("((my-block_id")
    const session = getSession(state)

    expect(session).toBeTruthy()
    expect(session?.trigger).toBe("((")
    expect(session?.query).toBe("my-block_id")
  })

  it("completes [[ pattern with spaces on second line", () => {
    const state = createTestState("line1\n[[Page Name]]")
    const session = getSession(state)

    expect(session).toBeNull()
  })
})
