import { describe, expect, it, vi } from "vitest"
import { useFocusRegistry } from "@/composables/useFocusRegistry"

describe("useFocusRegistry", () => {
  it("starts with no focused block", () => {
    const reg = useFocusRegistry()
    expect(reg.focusedId.value).toBeNull()
  })

  it("register and unregister a handle", () => {
    const reg = useFocusRegistry()
    const focus = vi.fn()
    reg.register("a", { focus })
    expect(reg.focus("a")).toBe(true)
    expect(focus).toHaveBeenCalledTimes(1)

    reg.unregister("a")
    expect(reg.focus("a")).toBe(false)
  })

  it("notifyFocused updates focusedId", () => {
    const reg = useFocusRegistry()
    reg.register("a", { focus: () => {} })
    reg.notifyFocused("a")
    expect(reg.focusedId.value).toBe("a")
  })

  it("focus calls handle and sets focusedId", () => {
    const reg = useFocusRegistry()
    const focus = vi.fn()
    reg.register("b", { focus })

    const result = reg.focus("b", "end")
    expect(result).toBe(true)
    expect(focus).toHaveBeenCalledWith("end")
    expect(reg.focusedId.value).toBe("b")
  })

  it("focus returns false for unknown id", () => {
    const reg = useFocusRegistry()
    expect(reg.focus("nonexistent")).toBe(false)
  })

  it("unregister clears focusedId if it was that block", () => {
    const reg = useFocusRegistry()
    reg.register("x", { focus: () => {} })
    reg.notifyFocused("x")
    expect(reg.focusedId.value).toBe("x")
    reg.unregister("x")
    expect(reg.focusedId.value).toBeNull()
  })

  describe("navigation", () => {
    it("focusFirst focuses the first registered block", () => {
      const reg = useFocusRegistry()
      const a = vi.fn()
      const b = vi.fn()
      reg.register("a", { focus: a })
      reg.register("b", { focus: b })

      expect(reg.focusFirst()).toBe(true)
      expect(a).toHaveBeenCalledTimes(1)
      expect(reg.focusedId.value).toBe("a")
    })

    it("focusFirst returns false when no blocks", () => {
      expect(useFocusRegistry().focusFirst()).toBe(false)
    })

    it("focusLast focuses the last registered block", () => {
      const reg = useFocusRegistry()
      const a = vi.fn()
      const b = vi.fn()
      reg.register("a", { focus: a })
      reg.register("b", { focus: b })

      expect(reg.focusLast()).toBe(true)
      expect(b).toHaveBeenCalledTimes(1)
      expect(reg.focusedId.value).toBe("b")
    })

    it("focusNext goes to next block in order", () => {
      const reg = useFocusRegistry()
      reg.register("a", { focus: () => {} })
      reg.register("b", { focus: () => {} })
      reg.register("c", { focus: () => {} })
      reg.notifyFocused("a")

      expect(reg.focusNext()).toBe(true)
      expect(reg.focusedId.value).toBe("b")

      expect(reg.focusNext()).toBe(true)
      expect(reg.focusedId.value).toBe("c")
    })

    it("focusNext wraps to first block", () => {
      const reg = useFocusRegistry()
      const a = vi.fn()
      reg.register("a", { focus: a })
      reg.register("b", { focus: () => {} })
      reg.notifyFocused("b")

      expect(reg.focusNext()).toBe(true)
      expect(a).toHaveBeenCalledTimes(1)
      expect(reg.focusedId.value).toBe("a")
    })

    it("focusNext with no focusedId goes to first", () => {
      const reg = useFocusRegistry()
      reg.register("a", { focus: () => {} })
      reg.register("b", { focus: () => {} })

      expect(reg.focusNext()).toBe(true)
      expect(reg.focusedId.value).toBe("a")
    })

    it("focusPrevious goes to previous block in order", () => {
      const reg = useFocusRegistry()
      reg.register("a", { focus: () => {} })
      reg.register("b", { focus: () => {} })
      reg.register("c", { focus: () => {} })
      reg.notifyFocused("c")

      expect(reg.focusPrevious()).toBe(true)
      expect(reg.focusedId.value).toBe("b")

      expect(reg.focusPrevious()).toBe(true)
      expect(reg.focusedId.value).toBe("a")
    })

    it("focusPrevious wraps to last block", () => {
      const reg = useFocusRegistry()
      reg.register("a", { focus: () => {} })
      const b = vi.fn()
      reg.register("b", { focus: b })
      reg.notifyFocused("a")

      expect(reg.focusPrevious()).toBe(true)
      expect(b).toHaveBeenCalledTimes(1)
      expect(reg.focusedId.value).toBe("b")
    })

    it("focusPrevious with no focusedId goes to last", () => {
      const reg = useFocusRegistry()
      reg.register("a", { focus: () => {} })
      reg.register("b", { focus: () => {} })

      expect(reg.focusPrevious()).toBe(true)
      expect(reg.focusedId.value).toBe("b")
    })
  })

  it("register does not duplicate ids", () => {
    const reg = useFocusRegistry()
    reg.register("a", { focus: () => {} })
    reg.register("a", { focus: () => {} })
    // After registering the same id twice, focusFirst should still work
    expect(reg.focusFirst()).toBe(true)
  })

  it("element is stored and accessible via focus", () => {
    const reg = useFocusRegistry()
    const el = document.createElement("div")
    const focus = vi.fn()
    reg.register("el", { focus, element: el })
    expect(reg.focus("el")).toBe(true)
    expect(focus).toHaveBeenCalledWith(undefined)
  })
})
