/**
 * Block keyboard handler for ProseMirror.
 *
 * Handles key events for block editor:
 * - Enter: split block (emit to parent)
 * - Shift+Enter: create new paragraph (soft line break) within the block
 *   Uses ProseMirror's splitBlock command to insert a new paragraph
 * - Backspace: chain of delete, join, merge-previous, delete-if-empty
 * - Delete: join forward
 * - Arrow keys: emit boundary events at document edges
 * - Tab/Shift+Tab: indent/outdent
 *
 * Uses ProseMirror commands for default behavior where possible.
 */

import {
  deleteSelection,
  joinBackward,
  joinForward,
} from "prosemirror-commands"
import type { EditorState } from "prosemirror-state"
import type { EditorView } from "prosemirror-view"
import { docToContent } from "@/lib/content-model"
import { schema } from "@/lib/schema"

export interface BlockKeyboardHandlers {
  split: (before: string, after: string) => void
  "merge-previous": () => void
  "delete-if-empty": () => void
  indent: () => void
  outdent: () => void
  "arrow-up-from-start": (leftOffset?: number) => void
  "arrow-down-from-end": (leftOffset?: number) => void
}

// Position detection helpers
function atDocumentStart(state: EditorState): boolean {
  return state.selection.head <= 1
}

function atParagraphStart(_state: EditorState, view: EditorView): boolean {
  return view.endOfTextblock("up")
}

function atParagraphEnd(_state: EditorState, view: EditorView): boolean {
  return view.endOfTextblock("down")
}

function isSingleEmptyParagraph(state: EditorState): boolean {
  const { doc } = state
  return doc.childCount === 1 && doc.child(0).content.size === 0
}

export function createBlockKeyboardHandler(handlers: BlockKeyboardHandlers) {
  return function handleKeyDown(
    view: EditorView,
    event: KeyboardEvent,
  ): boolean {
    const state = view.state

    // Shift+Enter: insert a new paragraph (soft line break) within the block.
    // Uses ProseMirror's splitBlock command, which creates a new paragraph
    // at the cursor position (inside the same block document).
    if (event.key === "Enter" && event.shiftKey) {
      const { $from } = state.selection
      const tr = state.tr
      // Split the current paragraph at cursor, creating a new paragraph
      tr.split($from.pos)
      view.dispatch(tr)
      view.focus()
      return true
    }

    // Enter: split block (create new Block.vue at parent level)
    // Uses ProseMirror slice operations to preserve all paragraphs,
    // not just the one containing the cursor. This prevents data loss
    // when splitting multi-paragraph blocks created via Shift+Enter.
    if (event.key === "Enter") {
      const { doc, selection } = state
      const { from } = selection

      // Slice document at cursor position and convert each half to markdown
      const beforeSlice = doc.slice(0, from)
      const afterSlice = doc.slice(from, doc.content.size)

      // Wrap slices in doc nodes so docToContent can process them
      const beforeDoc = schema.nodes.doc.create(null, beforeSlice.content)
      const afterDoc = schema.nodes.doc.create(null, afterSlice.content)

      const beforeStr = docToContent(beforeDoc)
      const afterStr = docToContent(afterDoc)

      handlers.split(beforeStr, afterStr)
      return true
    }

    // Backspace: chain of commands
    if (event.key === "Backspace" && state.selection.empty) {
      // Try default delete selection first
      if (deleteSelection(state, view.dispatch)) {
        return true
      }

      // At document start of single empty paragraph
      if (atDocumentStart(state) && isSingleEmptyParagraph(state)) {
        handlers["delete-if-empty"]()
        return true
      }

      // At document start of first paragraph
      if (atDocumentStart(state) && atParagraphStart(state, view)) {
        handlers["merge-previous"]()
        return true
      }

      // Otherwise try ProseMirror's joinBackward
      if (joinBackward(state, view.dispatch, view)) {
        return true
      }

      return false
    }

    // Delete: try ProseMirror's joinForward
    if (event.key === "Delete" && state.selection.empty) {
      if (joinForward(state, view.dispatch, view)) {
        return true
      }
      return false
    }

    // Tab: indent
    if (event.key === "Tab") {
      if (event.shiftKey) {
        handlers.outdent()
      } else {
        handlers.indent()
      }
      return true
    }

    // ArrowUp: emit at document start with X coordinate for column-preserving focus
    if (event.key === "ArrowUp" && state.selection.empty) {
      const $head = state.selection.$head
      const isFirstParagraph = $head.index($head.depth - 1) === 0

      if (isFirstParagraph && atParagraphStart(state, view)) {
        const coords = view.coordsAtPos(state.selection.head)
        handlers["arrow-up-from-start"](coords?.left)
        return true
      }
      return false
    }

    // ArrowDown: emit at document end with X coordinate for column-preserving focus
    if (event.key === "ArrowDown" && state.selection.empty) {
      const $head = state.selection.$head
      const docNode = $head.node($head.depth - 1)
      const isLastParagraph =
        $head.index($head.depth - 1) === docNode.childCount - 1

      if (isLastParagraph && atParagraphEnd(state, view)) {
        const coords = view.coordsAtPos(state.selection.head)
        handlers["arrow-down-from-end"](coords?.left)
        return true
      }
      return false
    }

    return false
  }
}
