import { useDraggable, useDroppable } from '@dnd-kit/core'
import { CSS } from '@dnd-kit/utilities'
import { motion } from 'framer-motion'
import { useRef } from 'react'

import TagLink from '@/components/TagLink'
import { clientAnimationTransition } from '@/lib/clientAnimation'
import { useClientBehaviourSettings } from '@/lib/useClientBehaviourSettings'
import { cn } from '@/lib/utils'

import type { CSSProperties, FC, MutableRefObject } from 'react'

import type { Tag } from '@/types'

type Props = {
    activeDndId?:      string
    tag:              Tag
    nestLevel:        number
    pathKey:          string
    parentTagId?:     number
    suppressClickRef: MutableRefObject<boolean>
    sp?:              boolean }


const DraggableDroppableTagRow: FC<Props> = ({
    activeDndId,
    tag,
    nestLevel,
    pathKey,
    parentTagId,
    suppressClickRef,
    sp,
}) => {
  const behaviourSettings = useClientBehaviourSettings ()
  const animationMode = behaviourSettings.animation ?? 'normal'
  const layoutTransition = clientAnimationTransition (
      animationMode,
      { normal: { duration: .2, ease: 'easeOut' as const } },
  )
  const dndId = `tag-node:${ pathKey }`

  const downPosRef = useRef<{ x: number; y: number } | null> (null)
  const armedRef = useRef (false)

  const armEatNextClick = () => {
    if (armedRef.current)
      return

    armedRef.current = true
    suppressClickRef.current = true

    const handler = (ev: MouseEvent) => {
      ev.preventDefault ()
      ev.stopPropagation ()
      armedRef.current = false
      suppressClickRef.current = false
    }

    addEventListener ('click', handler, { capture: true, once: true })
  }

  const { attributes,
	  listeners,
	  setNodeRef: setDragRef,
	  transform } = useDraggable ({ id:    dndId,
					data:  { kind:   'tag',
						 dndId,
						 tagId:  tag.id,
						 parentTagId,
						 nestLevel } })

  const { setNodeRef: setDropRef, isOver: over } = useDroppable ({
      id:   dndId,
      data: { kind: 'tag', tagId: tag.id } })

  const activeDragging = activeDndId === dndId
  const style: CSSProperties = { transform:   CSS.Translate.toString (transform),
				 visibility:  activeDragging ? 'hidden' : 'visible' }
  const innerClassName = cn (
      'inline-flex min-w-0 max-w-full items-baseline overflow-hidden',
      sp && 'touch-pan-y')

  return (
      <div
	  onPointerDownCapture={e => {
	    downPosRef.current = { x: e.clientX, y: e.clientY }
	  }}
	  onPointerMoveCapture={e => {
	    const p = downPosRef.current
	    if (!(p))
	      return
	    const dx = e.clientX - p.x
	    const dy = e.clientY - p.y

	    if (dx * dx + dy * dy >= 9)
	      armEatNextClick ()
	  }}
	  onPointerUpCapture={() => {
	    downPosRef.current = null
	  }}
	  onPointerCancelCapture={() => {
	    downPosRef.current = null
	  }}
	  onClickCapture={e => {
	    if (suppressClickRef.current)
	      {
		e.preventDefault ()
		e.stopPropagation ()
	      }
	  }}
	  ref={setDropRef}
	  className={cn (
	      'min-w-0 max-w-full overflow-hidden rounded select-none',
	      sp && 'touch-pan-y',
	      over && 'ring-2 ring-offset-2')}
	  >
	{activeDragging
	 ? (
	    <div
		ref={setDragRef}
		style={style}
		className={innerClassName}
		{...attributes}
		{...listeners}>
	      <TagLink tag={tag} nestLevel={nestLevel}/>
	    </div>)
	 : (
	    <motion.div
		ref={setDragRef}
		style={style}
		className={innerClassName}
		{...attributes}
		{...listeners}
		transition={{ layout: layoutTransition }}
		layoutId={animationMode === 'off'
		    ? undefined
		    : `tag-${ sp ? 'sp-' : '' }${ tag.id }`}>
	      <TagLink tag={tag} nestLevel={nestLevel}/>
	    </motion.div>)}
      </div>)
}

export default DraggableDroppableTagRow
