'use client'

import { Checkbox } from '@admin/components/ui/checkbox'
import { getTaskItemCheckboxLabel } from '@admin/utils/editor/get-task-item-checkbox-label'
import { hasOwnAttribute } from '@admin/utils/editor/has-own-attribute'
import { isTaskItemChecked } from '@admin/utils/editor/is-task-item-checked'
import { setElementAttribute } from '@admin/utils/editor/set-element-attribute'
import { getRenderedAttributes } from '@tiptap/core'
import { TaskItem } from '@tiptap/extension-list'
import { createRoot } from 'react-dom/client'

export const ContentEditorTaskItem = TaskItem.extend({
  addNodeView() {
    return ({ node, HTMLAttributes, getPos, editor }) => {
      let currentNode = node
      let renderedChecked = isTaskItemChecked(node)

      const listItem = document.createElement('li')
      const checkboxContainer = document.createElement('span')
      const checkboxMount = document.createElement('span')
      const content = document.createElement('div')
      const checkboxRoot = createRoot(checkboxMount)
      const staticAttributes = this.options.HTMLAttributes as Record<string, unknown>

      checkboxContainer.contentEditable = 'false'
      checkboxContainer.className = 'content-editor-task-item-checkbox'
      checkboxContainer.append(checkboxMount)
      listItem.append(checkboxContainer, content)

      const renderCheckbox = () => {
        const hasReadOnlyHandler = Boolean(this.options.onReadOnlyChecked)

        checkboxRoot.render(
          <Checkbox
            aria-label={getTaskItemCheckboxLabel(
              currentNode,
              renderedChecked,
              this.options.a11y?.checkboxLabel
            )}
            checked={renderedChecked}
            disabled={!editor.isEditable && !hasReadOnlyHandler}
            onMouseDown={(event) => event.preventDefault()}
            onCheckedChange={(checkedState) => {
              const checked = checkedState === true

              if (!editor.isEditable && !this.options.onReadOnlyChecked) {
                renderCheckbox()
                return
              }

              if (editor.isEditable && typeof getPos === 'function') {
                editor
                  .chain()
                  .focus(undefined, { scrollIntoView: false })
                  .command(({ tr }) => {
                    const position = getPos()
                    if (typeof position !== 'number') return false

                    const taskItem = tr.doc.nodeAt(position)
                    tr.setNodeMarkup(position, undefined, {
                      ...taskItem?.attrs,
                      checked
                    })
                    return true
                  })
                  .run()
                return
              }

              if (!editor.isEditable && this.options.onReadOnlyChecked?.(currentNode, checked)) {
                renderedChecked = checked
              }

              renderCheckbox()
            }}
          />
        )
      }

      function applyAttributes(nextHTMLAttributes: Record<string, unknown>) {
        for (const [key, value] of Object.entries(staticAttributes)) {
          setElementAttribute(listItem, key, value)
        }

        for (const [key, value] of Object.entries(nextHTMLAttributes)) {
          setElementAttribute(listItem, key, value)
        }

        listItem.dataset.checked = String(renderedChecked)
      }

      applyAttributes(HTMLAttributes as Record<string, unknown>)
      renderCheckbox()

      let prevRenderedAttributeKeys = new Set(Object.keys(HTMLAttributes))

      return {
        dom: listItem,
        contentDOM: content,
        update: (updatedNode) => {
          if (updatedNode.type !== this.type) return false

          currentNode = updatedNode
          renderedChecked = isTaskItemChecked(updatedNode)

          const extensionAttributes = editor.extensionManager.attributes
          const newHTMLAttributes = getRenderedAttributes(
            updatedNode,
            extensionAttributes
          ) as Record<string, unknown>
          const newKeys = new Set(Object.keys(newHTMLAttributes))

          prevRenderedAttributeKeys.forEach((key) => {
            if (newKeys.has(key)) return

            if (hasOwnAttribute(staticAttributes, key)) {
              setElementAttribute(listItem, key, staticAttributes[key])
              return
            }

            listItem.removeAttribute(key)
          })

          applyAttributes(newHTMLAttributes)
          prevRenderedAttributeKeys = newKeys
          renderCheckbox()

          return true
        },
        stopEvent: (event) => {
          return checkboxContainer.contains(event.target as Node | null)
        },
        ignoreMutation: (mutation) => {
          return checkboxContainer.contains(mutation.target)
        },
        destroy: () => {
          queueMicrotask(() => checkboxRoot.unmount())
        }
      }
    }
  }
})

export default ContentEditorTaskItem
