import { TextSelection } from "prosemirror-state"
import { describe, expect, it, vi } from "vitest"
import { createMockKeyEvent, createMockView } from "@/__test-utils__"
import {
  type BlockKeyboardHandlers,
  createBlockKeyboardHandler,
} from "@/composables/useBlockKeyboardHandlers"
import { contentToDoc } from "@/lib/content-model"

function createTestHandlers(): {
  handlers: BlockKeyboardHandlers
  emit: ReturnType<typeof vi.fn>
} {
  const emit = vi.fn()
  const handlers: BlockKeyboardHandlers = {
    split: (before, after) => emit("split", before, after),
    "merge-previous": () => emit("merge-previous"),
    "delete-if-empty": () => emit("delete-if-empty"),
    indent: () => emit("indent"),
    outdent: () => emit("outdent"),
    "arrow-up-from-start": () => emit("arrow-up-from-start"),
    "arrow-down-from-end": () => emit("arrow-down-from-end"),
  }
  return { handlers, emit }
}

describe("Block keyboard handler", () => {
  describe("Enter", () => {
    it("emits split with correct before/after when pressed in middle", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("hello world", 6)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Enter"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("split", "hello", " world")
    })

    it("preserves all paragraphs when splitting multi-paragraph block", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("line 1\nline 2\nline 3", 10)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Enter"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledTimes(1)
      const splitCall = emit.mock.calls[0]
      expect(splitCall[0]).toBe("split")
      const before = splitCall[1]
      const after = splitCall[2]
      expect(before).toContain("line 1")
      expect(after).toContain("line 3")
    })

    it("splits at correct position within single paragraph", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("first paragraph\nsecond paragraph", 8)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Enter"),
      )

      expect(result).toBe(true)
      const splitCall = emit.mock.calls[0]
      expect(splitCall[1]).toBe("first p")
      expect(splitCall[2]).toContain("second paragraph")
    })
  })

  describe("Shift+Enter", () => {
    it("splits paragraph within the block", () => {
      const { handlers } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)

      const doc = contentToDoc("hello world")
      const selection = TextSelection.create(doc, 6)

      // Use a real ProseMirror transaction to test the split
      const state = {
        doc,
        selection,
        schema: doc.type.schema,
        tr: undefined as any,
      }

      // Build a proper transaction
      const { EditorState } = require("prosemirror-state")
      const editorState = EditorState.create({ doc, plugins: [] })
      state.tr = editorState.tr

      const mockView = {
        state: editorState,
        coordsAtPos: () => ({ left: 100, top: 200 }),
        dispatch: vi.fn(),
        endOfTextblock: () => false,
        focus: vi.fn(),
      } as unknown as EditorView

      const result = handleKeyDown(
        mockView,
        createMockKeyEvent("Enter", { shiftKey: true }),
      )

      expect(result).toBe(true)
      expect(mockView.dispatch).toHaveBeenCalled()
    })
  })

  describe("Backspace at position 1", () => {
    it("emits merge-previous when at position 1", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("hello", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Backspace"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("merge-previous")
    })
  })

  describe("Tab", () => {
    it("emits indent when pressed", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Tab"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("indent")
    })

    it("emits outdent when shift+tab pressed", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("Tab", { shiftKey: true }),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("outdent")
    })
  })

  describe("Arrow keys", () => {
    it("emits arrow-up-from-start when arrow up at document start", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("hello", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("ArrowUp"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("arrow-up-from-start")
    })

    it("emits arrow-down-from-end when arrow down at document end", () => {
      const { handlers, emit } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("hello", 6)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("ArrowDown"),
      )

      expect(result).toBe(true)
      expect(emit).toHaveBeenCalledWith("arrow-down-from-end")
    })
  })

  describe("Pattern detection", () => {
    it("returns false for / (handled by plugin)", () => {
      const { handlers } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("/"),
      )

      expect(result).toBe(false)
    })

    it("returns false for [ (handled by plugin)", () => {
      const { handlers } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("[", 2)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("["),
      )

      expect(result).toBe(false)
    })

    it("returns false for # (handled by plugin)", () => {
      const { handlers } = createTestHandlers()
      const handleKeyDown = createBlockKeyboardHandler(handlers)
      const mockView = createMockView("", 1)

      const result = handleKeyDown(
        mockView as EditorView,
        createMockKeyEvent("#"),
      )

      expect(result).toBe(false)
    })
  })
})
