import type { Editor } from '@tiptap/core'
import { isAtStartOfNode, isNodeActive } from '@tiptap/core'
import type { Node } from '@tiptap/pm/model'

import { findListItemPos } from './findListItemPos.js'
import { hasListBefore } from './hasListBefore.js'
import { hasListItemBefore } from './hasListItemBefore.js'
import { listItemHasSubList } from './listItemHasSubList.js'

export const handleBackspace = (editor: Editor, name: string, parentListTypes: string[]) => {
  // this is required to still handle the undo handling
  if (editor.commands.undoInputRule()) {
    return true
  }

  // if the selection is not collapsed
  // we can rely on the default backspace behavior
  if (editor.state.selection.from !== editor.state.selection.to) {
    return false
  }

  // if the current item is NOT inside a list item &
  // the previous item is a list (orderedList or bulletList)
  // move the cursor into the list and delete the current item
  if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) {
    const { $anchor } = editor.state.selection

    const $listPos = editor.state.doc.resolve($anchor.before() - 1)

    const listDescendants: Array<{ node: Node; pos: number }> = []

    $listPos.node().descendants((node, pos) => {
      if (node.type.name === name) {
        listDescendants.push({ node, pos })
      }
    })

    const lastItem = listDescendants.at(-1)

    if (!lastItem) {
      return false
    }

    const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1)

    return editor
      .chain()
      .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end())
      .joinForward()
      .run()
  }

  // if the cursor is not inside the current node type
  // do nothing and proceed
  if (!isNodeActive(editor.state, name)) {
    return false
  }

  // if the cursor is not at the start of a node
  // do nothing and proceed
  if (!isAtStartOfNode(editor.state)) {
    return false
  }

  const listItemPos = findListItemPos(name, editor.state)

  if (!listItemPos) {
    return false
  }

  const $prev = editor.state.doc.resolve(listItemPos.$pos.pos - 2)
  const prevNode = $prev.node(listItemPos.depth)

  const previousListItemHasSubList = listItemHasSubList(name, editor.state, prevNode)

  // if the previous item is a list item and doesn't have a sublist, join the list items
  if (hasListItemBefore(name, editor.state) && !previousListItemHasSubList) {
    return editor.commands.joinItemBackward()
  }

  // otherwise in the end, a backspace should
  // always just lift the list item if
  // joining / merging is not possible
  return editor.chain().liftListItem(name).run()
}
