import { type CSSProperties, useMemo } from 'react';

import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { IconButton } from '@mui/material';
import { IconGripVertical } from '@tabler/icons-react';

import { type BaseSortableItem, type SortableItemProps } from '../types';

const SortableItem = <T extends BaseSortableItem>({
  item,
  ItemComponent,
  dragByHandler = false,
  style: inheritedStyle,
  hasDragOverlay = false,
  index,
  slotProps,
}: SortableItemProps<T>) => {
  const {
    attributes,
    listeners,
    setNodeRef,
    setActivatorNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: item.id });

  const hasDefaultDraggingStyles = isDragging && !hasDragOverlay;

  const style: CSSProperties = {
    position: 'relative',
    opacity: isDragging && hasDragOverlay ? 0.4 : undefined,
    transform: CSS.Transform.toString(transform),
    scale: hasDefaultDraggingStyles ? 1.05 : 1,
    zIndex: hasDefaultDraggingStyles ? 1 : 0,
    transition,
    listStyle: 'none',
    cursor: dragByHandler ? 'unset' : 'grab',
    ...inheritedStyle,
  };

  const handleProps = { ...listeners, ...attributes };

  const dragHandleButton = useMemo(
    () =>
      dragByHandler ? (
        <IconButton
          sx={{ cursor: isDragging ? 'grabbing' : 'grab' }}
          ref={setActivatorNodeRef}
          {...listeners}
          {...attributes}
          {...slotProps?.dragButton}
        >
          <IconGripVertical {...slotProps?.dragIcon} />
        </IconButton>
      ) : null,
    [
      dragByHandler,
      listeners,
      attributes,
      setActivatorNodeRef,
      slotProps?.dragButton,
      slotProps?.dragIcon,
      isDragging,
    ],
  );

  return (
    <li
      ref={setNodeRef}
      style={style}
      {...(dragByHandler ? {} : handleProps)}
    >
      <ItemComponent
        item={item}
        dragHandleButton={dragHandleButton}
        index={index}
        isDragging={isDragging}
      />
    </li>
  );
};

export default SortableItem;
