import type { EditorView } from "prosemirror-view"
import type { ComputedRef, Ref } from "vue"
import { nextTick, onUnmounted, ref, watch } from "vue"
import type { PatternOpenPayload } from "@/composables/usePatternPlugin"
import type { MentionData } from "@/composables/useSuggestionGroups"
import {
  useMentionGroups,
  useSlashGroups,
} from "@/composables/useSuggestionGroups"
import { type FormattingHandlers } from "@/lib/formatting"

export function useBlockSuggestionMenu(options: {
  localSession: Ref<PatternOpenPayload | null>
  menuRef: Ref<{ forwardKey: (key: string) => void } | null>
  menuWrapperRef: Ref<HTMLElement | null>
  formattingHandlers: Ref<FormattingHandlers | null>
  mentionData: ComputedRef<MentionData | null>
  getView: () => EditorView | null
}) {
  const menuPosition = ref({ x: 0, y: 0 })

  function closeSession() {
    options.localSession.value = null
  }

  const slashGroups = useSlashGroups(
    options.localSession,
    options.formattingHandlers,
    closeSession,
  )
  const mentionGroups = useMentionGroups(
    options.localSession,
    closeSession,
    options.mentionData,
  )

  watch(options.localSession, (session) => {
    if (session && session.kind !== "command") {
      nextTick(() => {
        if (!options.localSession.value) return
        const q = options.localSession.value.query.toLowerCase()
        const hasMatches = mentionGroups.value.some((g) =>
          g.items?.some((item) => item.label?.toLowerCase().includes(q)),
        )
        if (!hasMatches) closeSession()
      })
    }
  })

  function onCaptureKeydown(e: KeyboardEvent) {
    if (!options.localSession.value) return
    if (["ArrowDown", "ArrowUp", "Enter", "Escape"].includes(e.key)) {
      e.preventDefault()
      e.stopPropagation()
      if (e.key === "Escape") {
        options.localSession.value?.close("cancelled")
        options.localSession.value = null
      } else {
        options.menuRef.value?.forwardKey(e.key)
      }
    }
  }

  function recalculateMenuPosition() {
    const view = options.getView()
    if (!options.localSession.value || !view || !options.menuWrapperRef.value) return
    const pos = view.coordsAtPos(options.localSession.value.range.from)
    if (!pos) return
    menuPosition.value = { x: pos.left, y: pos.top }
  }

  watch(options.localSession, (session, prev) => {
    if (session && !prev) {
      document.addEventListener("keydown", onCaptureKeydown, true)
      window.addEventListener("scroll", recalculateMenuPosition, { passive: true })
      recalculateMenuPosition()
    } else if (!session && prev) {
      document.removeEventListener("keydown", onCaptureKeydown, true)
      window.removeEventListener("scroll", recalculateMenuPosition)
    }
  })

  onUnmounted(() => {
    document.removeEventListener("keydown", onCaptureKeydown, true)
    window.removeEventListener("scroll", recalculateMenuPosition)
  })

  return { slashGroups, mentionGroups, menuPosition, closeSession }
}
