import React, { useRef, useState } from 'react';
import { useDrag, useDrop } from 'react-dnd';
import classnames from 'classnames';
import { getNonNativeButtonAttrs } from '../hooks';
import { DragNDropListItemContent } from './drag-and-drop-list-item-content';
import { jsx as _jsx } from "react/jsx-runtime";
const DND_ITEM_TYPE = 'DND_ITEM_TYPE';
export function DragNDropListItem({
  id,
  index,
  listItem,
  getIndexById,
  onItemMove,
  canDrag,
  canDrop,
  onDragEnd,
  ListItemComponent,
  isAnyItemDragging,
  setIsAnyItemDragging,
  listItemClassName,
  shouldUseSeparateDragRef,
  isActive,
  isError,
  ListItemContent = DragNDropListItemContent,
  dragItemType = DND_ITEM_TYPE,
  additionalListItemProps = {}
}) {
  // this is needed as a workaround for WebKit bug with drag n drop and hovering:
  // https://bugs.webkit.org/show_bug.cgi?id=134555
  const [isHovered, setIsHovered] = useState(false);
  const dropRef = useRef(null);
  const dragRef = useRef(null);
  const [, drop] = useDrop({
    accept: dragItemType,
    hover(item, monitor) {
      if (!dropRef.current) {
        return;
      }
      // To avoid wrong ordering with too fast dragging and updates during dragging
      item.index = getIndexById(item.id);
      const dragIndex = item.index;
      const hoverIndex = index;
      if (!isAnyItemDragging) {
        setIsAnyItemDragging(true);
      }

      // Don't replace items with themselves
      if (dragIndex === hoverIndex) {
        return;
      }

      // Determine rectangle on screen
      const hoverBoundingRect = dropRef.current.getBoundingClientRect();
      // Get vertical middle
      const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
      // Determine mouse position
      const clientOffset = monitor.getClientOffset();
      // Get pixels to the top
      const hoverClientY = (clientOffset?.y || 0) - hoverBoundingRect.top;
      // Only perform the move when the mouse has crossed half of the items height
      // When dragging downwards, only move when the cursor is below 50%
      // When dragging upwards, only move when the cursor is above 50%
      // Dragging downwards
      if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
        return;
      }
      // Dragging upwards
      if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
        return;
      }
      // Time to actually perform the action
      onItemMove(dragIndex, hoverIndex);
      item.index = hoverIndex;
    },
    canDrop: item => {
      return canDrop?.(listItem, item.index) ?? true;
    }
  });
  const [collected, drag] = useDrag({
    type: dragItemType,
    item: {
      id,
      index
    },
    collect: monitor => {
      const isDragging = !!monitor.isDragging();
      return {
        isDragging
      };
    },
    canDrag() {
      return canDrag?.(listItem, index) ?? true;
    },
    end(item) {
      setIsAnyItemDragging(false);
      if (onDragEnd) {
        onDragEnd(listItem, item.index);
      }
    }
  });

  // if the whole list component is provided - render only it with appropriate props
  if (ListItemComponent) {
    const draggableRef = shouldUseSeparateDragRef ? dragRef : dropRef;
    drag(draggableRef);
    drop(dropRef);
    return /*#__PURE__*/_jsx(ListItemComponent, {
      listItem: listItem,
      index: index,
      isDragging: collected.isDragging,
      dropRef: dropRef,
      dragRef: dragRef,
      isAnyItemDragging: isAnyItemDragging,
      ...additionalListItemProps
    });
  }
  drag(dropRef);
  drop(dropRef);
  return /*#__PURE__*/_jsx("div", {
    ref: dropRef,
    className: classnames('drag-n-drop-list-item', listItemClassName, {
      'drag-n-drop-list-item-active': isActive,
      'drag-n-drop-list-item-error': isError,
      'drag-n-drop-list-item-dragging': collected.isDragging,
      'drag-n-drop-list-item-hovered': isHovered && !isAnyItemDragging
    }),
    ...getNonNativeButtonAttrs({
      onClick: () => setIsHovered(true),
      onKeyDown: () => setIsHovered(true)
    }),
    onMouseEnter: () => !isAnyItemDragging && setIsHovered(true),
    onMouseLeave: () => setIsHovered(false),
    children: /*#__PURE__*/_jsx("div", {
      className: "drag-n-drop-list-content-wrapper",
      children: /*#__PURE__*/_jsx(ListItemContent, {
        listItem: listItem,
        index: index,
        isDragging: collected.isDragging,
        isAnyItemDragging: isAnyItemDragging,
        ...additionalListItemProps
      })
    })
  });
}