import type { JSX } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { DraggableBlockPlugin_EXPERIMENTAL } from '@lexical/react/LexicalDraggableBlockPlugin'
import { useEffect, useRef, useState } from 'react'

const DRAGGABLE_BLOCK_MENU_CLASSNAME = 'draggable-block-menu'

function isOnMenu(element: HTMLElement): boolean {
  return !!element.closest(`.${DRAGGABLE_BLOCK_MENU_CLASSNAME}`)
}

const SUPPORT_DRAG_CLASS = 'support-drag'
function checkSupportDrag(element: Element | null): boolean {
  if (!element)
    return false

  if (element.classList.contains(SUPPORT_DRAG_CLASS))
    return true

  if (element.querySelector(`.${SUPPORT_DRAG_CLASS}`))
    return true

  return !!(element.closest(`.${SUPPORT_DRAG_CLASS}`))
}

export default function DraggableBlockPlugin({
  anchorElem = document.body,
}: {
  anchorElem?: HTMLElement
}): JSX.Element {
  const menuRef = useRef<HTMLDivElement>(null)
  const targetLineRef = useRef<HTMLDivElement>(null)
  const [, setDraggableElement] = useState<HTMLElement | null>(
    null,
  )
  const [editor] = useLexicalComposerContext()

  const [isSupportDrag, setIsSupportDrag] = useState(false)

  useEffect(() => {
    const root = editor.getRootElement()
    if (!root)
      return

    const onMove = (e: MouseEvent) => {
      const isSupportDrag = checkSupportDrag(e.target as Element)
      setIsSupportDrag(isSupportDrag)
    }

    root.addEventListener('mousemove', onMove)
    return () => root.removeEventListener('mousemove', onMove)
  }, [editor])

  return (
    <DraggableBlockPlugin_EXPERIMENTAL
      anchorElem={anchorElem}
      menuRef={menuRef as any}
      targetLineRef={targetLineRef as any}
      menuComponent={
        isSupportDrag
          ? (
              <div ref={menuRef} className={cn(DRAGGABLE_BLOCK_MENU_CLASSNAME, 'absolute top-4 right-2.5 cursor-grab opacity-0 will-change-transform active:cursor-move')} data-testid="draggable-menu">
                <span className="i-ri-draggable size-3.5 text-text-tertiary" data-testid="draggable-menu-icon" />
              </div>
            )
          : null
      }
      targetLineComponent={(
        <div
          ref={targetLineRef}
          className="pointer-events-none absolute top-0 left-[-21px] opacity-0 will-change-transform"
          data-testid="draggable-target-line"
          // style={{ width: 500 }} // width not worked here
        >
          <div
            className="absolute top-0 -right-10 left-0 h-[2px] bg-text-accent-secondary"
          >
          </div>
        </div>
      )}
      isOnMenu={isOnMenu}
      onElementChanged={setDraggableElement}
    />
  )
}
