'use client'

import * as React from 'react'
import type { ContentEditorTableAlignment } from '@admin/utils/editor/content-editor-table-alignment'
import type { Editor } from '@tiptap/core'
import { Button } from '@admin/components/ui/button'
import { Separator } from '@admin/components/ui/separator'
import { useTiptapEditor } from '@admin/hooks/content-editor/use-tiptap-editor'
import { clearTablePartPreview } from '@admin/utils/editor/clear-table-part-preview'
import { getMountedEditorView } from '@admin/utils/editor/editor-view'
import { getActiveTableColumnAlignment } from '@admin/utils/editor/get-active-table-column-alignment'
import { getClosestTableCell } from '@admin/utils/editor/get-closest-table-cell'
import { getEditorElement } from '@admin/utils/editor/get-editor-element'
import { getEditorScrollElement } from '@admin/utils/editor/get-editor-scroll-element'
import { getEventTableCell } from '@admin/utils/editor/get-event-table-cell'
import { isSelectedTableVisible } from '@admin/utils/editor/is-selected-table-visible'
import { setActiveTableColumnAlignment } from '@admin/utils/editor/set-active-table-column-alignment'
import { showTablePartPreview } from '@admin/utils/editor/show-table-part-preview'
import { cn } from '@admin/utils/shared/cn'
import { BubbleMenu } from '@tiptap/react/menus'
import {
  AlignCenterIcon,
  AlignLeftIcon,
  AlignRightIcon,
  Columns3Icon,
  Rows3Icon
} from 'lucide-react'
import { RemoveTablePartIcon } from './remove-table-part-icon'

const ALIGNMENT_CONTROLS: Array<{
  alignment: ContentEditorTableAlignment
  Icon: React.ComponentType<{ className?: string }>
  label: string
}> = [
  {
    alignment: 'left',
    Icon: AlignLeftIcon,
    label: 'Align column left'
  },
  {
    alignment: 'center',
    Icon: AlignCenterIcon,
    label: 'Align column center'
  },
  {
    alignment: 'right',
    Icon: AlignRightIcon,
    label: 'Align column right'
  }
]

export function TableBubbleMenu({ editor }: { editor: Editor }) {
  const { editor: currentEditor } = useTiptapEditor(editor)
  const tableEditor = currentEditor ?? editor
  const activeAlignment = getActiveTableColumnAlignment(tableEditor)
  const [isSelectedTableInView, setIsSelectedTableInView] = React.useState(false)
  const [isTypingInCell, setIsTypingInCell] = React.useState(false)
  const [, markEditorViewChanged] = React.useReducer((version: number) => version + 1, 0)
  const isTypingInCellRef = React.useRef(false)
  const isEditorViewMounted = Boolean(getMountedEditorView(editor))

  const updateIsTypingInCell = React.useCallback((nextValue: boolean) => {
    if (isTypingInCellRef.current === nextValue) return

    isTypingInCellRef.current = nextValue
    setIsTypingInCell(nextValue)
  }, [])

  const updateSelectedTableVisibility = React.useCallback(() => {
    setIsSelectedTableInView(editor.isActive('table') && isSelectedTableVisible(editor))
  }, [editor])

  React.useEffect(
    () => () => {
      clearTablePartPreview(editor)
    },
    [editor]
  )

  React.useEffect(() => {
    const handleEditorViewChange = () => markEditorViewChanged()

    editor.on('mount', handleEditorViewChange)
    editor.on('unmount', handleEditorViewChange)
    if (getMountedEditorView(editor)) {
      handleEditorViewChange()
    }

    return () => {
      editor.off('mount', handleEditorViewChange)
      editor.off('unmount', handleEditorViewChange)
    }
  }, [editor])

  React.useEffect(() => {
    if (!isEditorViewMounted) {
      setIsSelectedTableInView(false)
      return
    }

    const scrollElement = getEditorScrollElement(editor)
    const editorElement = getEditorElement(editor)
    if (!scrollElement || !editorElement) {
      setIsSelectedTableInView(false)
      return
    }

    const ownerDocument = editorElement.ownerDocument

    scrollElement.addEventListener('scroll', updateSelectedTableVisibility, {
      passive: true
    })
    ownerDocument.addEventListener('scroll', updateSelectedTableVisibility, {
      capture: true,
      passive: true
    })
    editor.on('selectionUpdate', updateSelectedTableVisibility)
    editor.on('transaction', updateSelectedTableVisibility)
    updateSelectedTableVisibility()

    return () => {
      scrollElement.removeEventListener('scroll', updateSelectedTableVisibility)
      ownerDocument.removeEventListener('scroll', updateSelectedTableVisibility, true)
      editor.off('selectionUpdate', updateSelectedTableVisibility)
      editor.off('transaction', updateSelectedTableVisibility)
    }
  }, [editor, isEditorViewMounted, updateSelectedTableVisibility])

  React.useEffect(() => {
    if (!isEditorViewMounted) {
      updateIsTypingInCell(false)
      return
    }

    const editorElement = getEditorElement(editor)
    if (!editorElement) {
      updateIsTypingInCell(false)
      return
    }

    const suppressForCellInput = (event: Event) => {
      const cell = getEventTableCell(editor, event.target)
      if (!cell || !editorElement.contains(cell)) return

      clearTablePartPreview(editor)
      updateIsTypingInCell(true)
    }

    const showForCellClick = (event: Event) => {
      const cell = getClosestTableCell(event.target)
      if (!cell || !editorElement.contains(cell)) return

      updateIsTypingInCell(false)
    }

    editorElement.addEventListener('beforeinput', suppressForCellInput)
    editorElement.addEventListener('click', showForCellClick)
    editorElement.addEventListener('compositionstart', suppressForCellInput)
    editorElement.addEventListener('drop', suppressForCellInput)
    editorElement.addEventListener('input', suppressForCellInput)
    editorElement.addEventListener('mousedown', showForCellClick)
    editorElement.addEventListener('paste', suppressForCellInput)
    editorElement.addEventListener('pointerdown', showForCellClick)

    return () => {
      editorElement.removeEventListener('beforeinput', suppressForCellInput)
      editorElement.removeEventListener('click', showForCellClick)
      editorElement.removeEventListener('compositionstart', suppressForCellInput)
      editorElement.removeEventListener('drop', suppressForCellInput)
      editorElement.removeEventListener('input', suppressForCellInput)
      editorElement.removeEventListener('mousedown', showForCellClick)
      editorElement.removeEventListener('paste', suppressForCellInput)
      editorElement.removeEventListener('pointerdown', showForCellClick)
    }
  }, [editor, isEditorViewMounted, updateIsTypingInCell])

  return (
    isEditorViewMounted &&
    isSelectedTableInView && (
      <BubbleMenu
        editor={editor}
        pluginKey="tableBubbleMenu"
        updateDelay={0}
        shouldShow={({ editor: nextEditor }) =>
          nextEditor.isEditable &&
          nextEditor.isFocused &&
          nextEditor.isActive('table') &&
          !isTypingInCell
        }
        options={{
          flip: { padding: 8 },
          offset: 8,
          placement: 'top',
          shift: { padding: 8 }
        }}
      >
        <div
          role="toolbar"
          aria-label="Table formatting toolbar"
          className={cn(
            'flex items-center gap-1 overflow-x-auto rounded-sm border border-border bg-popover p-1 text-popover-foreground shadow-menu [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
            'relative z-50'
          )}
        >
          <div className="flex items-center gap-0.5">
            {ALIGNMENT_CONTROLS.map(({ alignment, Icon, label }) => {
              const isActive = activeAlignment === alignment

              return (
                <Button
                  key={alignment}
                  type="button"
                  variant="ghost"
                  size="icon-sm"
                  aria-label={label}
                  aria-pressed={isActive}
                  className={cn({
                    'bg-accent text-accent-foreground': isActive
                  })}
                  onPointerDown={(event) => event.preventDefault()}
                  onClick={() => {
                    setActiveTableColumnAlignment(tableEditor, alignment)
                  }}
                  title={label}
                >
                  <Icon />
                </Button>
              )
            })}
          </div>
          <Separator
            orientation="vertical"
            className="mx-1 my-auto h-5 w-px self-center bg-border opacity-100"
          />
          <div className="flex items-center gap-0.5">
            <Button
              type="button"
              variant="ghost"
              size="icon-sm"
              aria-label="Remove table row"
              onBlur={() => clearTablePartPreview(tableEditor)}
              onFocus={() => showTablePartPreview(tableEditor, 'row')}
              onMouseEnter={() => showTablePartPreview(tableEditor, 'row')}
              onMouseLeave={() => clearTablePartPreview(tableEditor)}
              onMouseMove={() => showTablePartPreview(tableEditor, 'row')}
              onPointerDown={(event) => event.preventDefault()}
              onPointerEnter={() => showTablePartPreview(tableEditor, 'row')}
              onPointerLeave={() => clearTablePartPreview(tableEditor)}
              onPointerMove={() => showTablePartPreview(tableEditor, 'row')}
              onClick={() => {
                clearTablePartPreview(tableEditor)
                tableEditor.chain().focus().deleteRow().run()
              }}
              title="Remove Row"
            >
              <RemoveTablePartIcon Icon={Rows3Icon} />
            </Button>
            <Button
              type="button"
              variant="ghost"
              size="icon-sm"
              aria-label="Remove table column"
              onBlur={() => clearTablePartPreview(tableEditor)}
              onFocus={() => showTablePartPreview(tableEditor, 'column')}
              onMouseEnter={() => showTablePartPreview(tableEditor, 'column')}
              onMouseLeave={() => clearTablePartPreview(tableEditor)}
              onMouseMove={() => showTablePartPreview(tableEditor, 'column')}
              onPointerDown={(event) => event.preventDefault()}
              onPointerEnter={() => showTablePartPreview(tableEditor, 'column')}
              onPointerLeave={() => clearTablePartPreview(tableEditor)}
              onPointerMove={() => showTablePartPreview(tableEditor, 'column')}
              onClick={() => {
                clearTablePartPreview(tableEditor)
                tableEditor.chain().focus().deleteColumn().run()
              }}
              title="Remove Column"
            >
              <RemoveTablePartIcon Icon={Columns3Icon} />
            </Button>
          </div>
        </div>
      </BubbleMenu>
    )
  )
}
