import { useState } from 'react';

import type { DragEvent, ReactNode } from 'react';

import { Draggable } from './Draggable';

interface DraggableContainerProps<T extends { name: string }> {
  children: (item: T) => ReactNode;
  items: T[];
  onChange: (items: T[]) => void;
}

export function DraggableContainer<T extends { name: string }>({
  children,
  items,
  onChange,
}: DraggableContainerProps<T>) {
  const [dragIndex, setDragIndex] = useState<number | null>(null);

  const onDragStart = (index: number) => {
    setDragIndex(index);
  };

  const onDragOver = (e: DragEvent, index: number) => {
    e.preventDefault();
    if (dragIndex === null || dragIndex === index) return;

    // Only reorder when the cursor crosses the midpoint of the target item.
    // This prevents jumpy reordering with items of varying heights.
    const target = (e.currentTarget as HTMLElement).getBoundingClientRect();
    const midY = target.top + target.height / 2;

    // Moving down: only swap when cursor is past the midpoint
    // Moving up: only swap when cursor is above the midpoint
    if (dragIndex < index && e.clientY < midY) return;
    if (dragIndex > index && e.clientY > midY) return;

    const next = [...items];
    const [moved] = next.splice(dragIndex, 1);
    next.splice(index, 0, moved);

    onChange(next);
    setDragIndex(index);
  };

  const onDragEnd = () => {
    setDragIndex(null);
  };

  return (
    <>
      {items.map((item, index) => (
        <Draggable
          key={item.name}
          index={index}
          dragIndex={dragIndex}
          onDragStart={onDragStart}
          onDragEnd={onDragEnd}
          onDragOver={onDragOver}
        >
          {children(item)}
        </Draggable>
      ))}
    </>
  );
}
