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}
    />
  )
}
