import { useEffect, MutableRefObject, useState, useRef } from 'react';
import { useCallbackRef } from '../use-callback-ref';
import { getDragData } from './use-drag';

export type SilkeDropState = 'none' | 'over' | 'drop';

export function useDrop<T>({
  ref,
  onOver,
  onDrop,
  canDrop,
  disabled,
}: {
  ref: MutableRefObject<HTMLElement | undefined | null>;
  onOver?: (item: T, e: DragEvent) => void;
  onDrop?: (item: T, e: DragEvent) => void;
  canDrop?: (item: T, e: DragEvent, state: SilkeDropState) => boolean;
  disabled?: boolean;
}): SilkeDropState {
  const [state, setState] = useState<SilkeDropState>('none');
  const stateRef = useRef<SilkeDropState>(state);
  stateRef.current = state;
  const onDropRef = useCallbackRef(onDrop);
  const onOverRef = useCallbackRef(onOver);
  const canDropRef = useCallbackRef(canDrop);

  useEffect(() => {
    const el = ref.current;
    if (el && !disabled) {
      const handleDragEnter = (e: DragEvent) => {
        (e.dataTransfer as any).dropEffect = 'move';
        e.stopPropagation();
        e.preventDefault();
        const data = getDragData<T>();
        if (stateRef.current !== 'over' && canDropRef.current?.(data?.current, e, 'over')) {
          stateRef.current = 'over';
          setState('over');

          onOverRef.current?.(data?.current, e);
        }
      };

      const handleDrop = (e: DragEvent) => {
        e.stopPropagation();
        const data = getDragData<T>();
        if (canDropRef.current?.(data?.current, e, 'drop')) {
          stateRef.current = 'none';
          setState('none');
          onDropRef.current?.(data?.current, e);
        }
      };

      const handleDragLeave = () => {
        stateRef.current = 'none';
        setState('none');
      };

      el.addEventListener('dragenter', handleDragEnter);
      el.addEventListener('dragover', handleDragEnter);
      el.addEventListener('dragleave', handleDragLeave);
      el.addEventListener('drop', handleDrop);

      return () => {
        el.removeEventListener('dragenter', handleDragEnter);
        el.removeEventListener('dragover', handleDragEnter);
        el.removeEventListener('dragleave', handleDragLeave);
        el.removeEventListener('drop', handleDrop);
      };
    }
  }, [onDropRef, onOverRef, canDropRef, ref, disabled]);

  return state;
}
