import React, { useRef } from 'react';
import { useDrag, useDrop } from '../../hooks';
import styles from './silke-sortable-list.scss';

type SilkeSortableItemWrapperProps<T> = {
  items: T[];
  index: number;
  listId: string;
  triggerState: 'over' | 'drop';
  className?: string;
  children: React.ReactElement;
  onSort: (sortedItems: T[]) => void;
  disabled?: boolean;
};

type SortData<T> = {
  listId: string;
  index: number;
  items: T[];
  onSort: (sortedItems: T[]) => void;
};

export function SilkeSortableItemWrapper<T>({
  index,
  items,
  listId,
  children,
  triggerState,
  className,
  disabled,
  onSort,
}: SilkeSortableItemWrapperProps<T>) {
  const ref = useRef<HTMLDivElement>(null);
  const data: SortData<T> = { listId, index, items, onSort };
  const dragState = useDrag({ data, ref, disabled, isolated: true });

  const handleSort = (from: SortData<T>) => {
    const toList = items.slice();
    const fromList = items !== from.items ? from.items.slice() : toList;
    const [item] = fromList.splice(from.index, 1);
    if (fromList !== toList) {
      from.onSort(fromList);
      // If the item is removed from the fromList, it will give us an outdated version of the fromList
      // WHich causes the item to be inserted multiple times in the toList
      // So we need to remove all the items from the toList before inserting
      let index: number;
      while ((index = toList.indexOf(item)) !== -1) {
        toList.splice(index, 1);
      }
    }

    toList.splice(index, 0, item);
    onSort(toList);
  };

  const dropState = useDrop<SortData<T>>({
    ref,
    canDrop: (item) => item.listId === listId,
    onOver: (from) => triggerState === 'over' && handleSort(from),
    onDrop: (from) => triggerState === 'drop' && handleSort(from),
    disabled,
  });

  let cl = styles.item;
  if (className) cl += ' ' + className;
  if (dragState === 'dragging') cl += ' ' + styles.dragging;
  else if (dropState === 'over') cl += ' ' + styles.over;

  return (
    <div ref={ref} className={cl}>
      {children}
    </div>
  );
}
