import type { Node as ProsemirrorNode } from "prosemirror-model"
import { TextSelection } from "prosemirror-state"
import type { EditorView, NodeView } from "prosemirror-view"
import { getMathContent, renderKatex } from "@/lib/katex"

export class MathBlockView implements NodeView {
  dom: HTMLElement
  contentDOM: HTMLElement
  private preview: HTMLElement
  private pmView: EditorView
  private getPos: () => number

  constructor(node: ProsemirrorNode, view: EditorView, getPos: () => number) {
    this.pmView = view
    this.getPos = getPos

    this.dom = document.createElement("div")
    this.dom.className = "math-block"

    this.preview = document.createElement("div")
    this.preview.className = "math-block-preview"

    this.contentDOM = document.createElement("div")
    this.contentDOM.className = "math-block-source"

    this.dom.append(this.preview, this.contentDOM)

    this.renderPreview(node.textContent)

    this.contentDOM.addEventListener("keydown", (e) => {
      const $pos = this.pmView.state.selection.$from
      if ($pos.parent.type.name !== "math_block") return

      const atEnd = $pos.parentOffset >= $pos.parent.content.size

      if (e.key === "ArrowUp" && $pos.parentOffset === 0) {
        e.preventDefault()
        e.stopPropagation()
        this.exitBlock("up")
        return
      }

      if (e.key === "ArrowDown" && atEnd) {
        e.preventDefault()
        e.stopPropagation()
        this.exitBlock("down")
        return
      }

      if (e.key === "Enter" && atEnd) {
        const text = $pos.parent.textContent
        const beforeCursor = text.slice(0, $pos.parentOffset)
        const lineStart = beforeCursor.lastIndexOf("\n") + 1
        const currentLine = beforeCursor.slice(lineStart)
        if (/^\$\$\s*$/.test(currentLine)) {
          e.preventDefault()
          e.stopPropagation()
          this.exitBlock("down")
          return
        }
      }
    })
  }

  private renderPreview(source: string) {
    this.preview.textContent = ""
    const math = getMathContent(source)
    if (math) renderKatex(math, this.preview, true)
  }

  private exitBlock(dir: "up" | "down") {
    const pos = this.getPos()
    const node = this.pmView.state.doc.nodeAt(pos)
    if (!node) return
    const targetPos = dir === "up" ? pos - 1 : pos + node.nodeSize
    if (targetPos < 0 || targetPos > this.pmView.state.doc.content.size) return
    const $pos = this.pmView.state.doc.resolve(targetPos)
    const sel = TextSelection.near($pos, dir === "up" ? -1 : 1)
    if (!sel) return
    this.pmView.dispatch(
      this.pmView.state.tr.setSelection(sel).scrollIntoView(),
    )
    this.pmView.focus()
  }

  update(node: ProsemirrorNode) {
    if (node.type.name !== "math_block") return false
    this.renderPreview(node.textContent)
    return true
  }

  destroy() {
    // PM handles cleanup
  }

  stopEvent() {
    return true
  }
}
