import { jsx, jsxs } from 'react/jsx-runtime';
import React__default, { createContext, useState, useRef, useMemo, useCallback, useEffect, useContext } from 'react';
import { Reorder, useDragControls, motion } from 'motion/react';
import { LockClosedIcon, Bars3Icon } from '@heroicons/react/24/outline';
import { cn } from '../../../utils/cn.js';
import { sortableTheme } from './Sortable.theme.js';
import { sortableAnimations } from './Sortable.animations.js';

const SortableContext = createContext(null);
const useSortableContext = () => {
    const context = useContext(SortableContext);
    if (!context) {
        throw new Error('SortableItem must be used within a Sortable component');
    }
    return context;
};
const Sortable = React__default.forwardRef(({ children, onReorder, className, theme: customTheme = {}, direction = 'vertical', showDragHandle = false, onSortDragStart, onSortDragEnd, values, }, ref) => {
    const [draggedItem, setDraggedItem] = useState(null);
    const [dropPosition, setDropPosition] = useState(null);
    const itemRefs = useRef(new Map());
    const [draggedItemContent, setDraggedItemContent] = useState(null);
    const theme = {
        ...sortableTheme,
        ...customTheme,
    };
    // Extract values from children if not provided
    const internalValues = useMemo(() => {
        if (values)
            return values;
        const childArray = React__default.Children.toArray(children);
        return childArray.map((child) => {
            if (React__default.isValidElement(child)) {
                return child.props.value !== undefined ? child.props.value : child.props.id;
            }
            return null;
        }).filter((v) => v != null);
    }, [values, children]);
    const handleReorder = useCallback((newValues) => {
        // Calculate indices for the callback
        const oldValues = internalValues;
        if (oldValues.length === 0)
            return;
        // Find what moved
        for (let i = 0; i < newValues.length; i++) {
            const oldIndex = oldValues.indexOf(newValues[i]);
            if (oldIndex !== i) {
                onReorder(oldIndex, i);
                break;
            }
        }
    }, [internalValues, onReorder]);
    const contextValue = useMemo(() => ({
        direction,
        showDragHandle,
        theme,
        onReorder,
        onSortDragStart,
        onSortDragEnd,
        draggedItem,
        setDraggedItem,
        dropPosition,
        setDropPosition,
        itemRefs,
        draggedItemContent,
        setDraggedItemContent,
    }), [direction, showDragHandle, theme, onReorder, onSortDragStart, onSortDragEnd, draggedItem, dropPosition, draggedItemContent]);
    return (jsx(SortableContext.Provider, { value: contextValue, children: jsx(Reorder.Group, { ref: ref, axis: direction === 'horizontal' ? 'x' : 'y', values: internalValues, onReorder: handleReorder, className: cn(theme.containerStyle, direction === 'horizontal' ? 'flex flex-row' : 'flex flex-col', className), as: "div", children: children }) }));
});
Sortable.displayName = 'Sortable';
const SortableItem = React__default.forwardRef(({ id, children, className, theme: customTheme = {}, disabled = false, value, }, forwardedRef) => {
    const context = useSortableContext();
    const dragControls = useDragControls();
    const itemRef = useRef(null);
    const [isDragging, setIsDragging] = useState(false);
    const [isFocused, setIsFocused] = useState(false);
    const theme = {
        ...context.theme,
        ...customTheme,
    };
    // Register item ref
    useEffect(() => {
        if (itemRef.current) {
            context.itemRefs.current.set(id, itemRef.current);
        }
        return () => {
            context.itemRefs.current.delete(id);
        };
    }, [id, context.itemRefs]);
    // Keyboard navigation
    const handleKeyDown = useCallback((event) => {
        var _a, _b;
        if (disabled)
            return;
        const items = Array.from(context.itemRefs.current.entries());
        const currentIndex = items.findIndex(([itemId]) => itemId === id);
        if (currentIndex === -1)
            return;
        let targetIndex = currentIndex;
        switch (event.key) {
            case 'ArrowUp':
                if (context.direction === 'vertical' && currentIndex > 0) {
                    event.preventDefault();
                    targetIndex = currentIndex - 1;
                }
                break;
            case 'ArrowDown':
                if (context.direction === 'vertical' && currentIndex < items.length - 1) {
                    event.preventDefault();
                    targetIndex = currentIndex + 1;
                }
                break;
            case 'ArrowLeft':
                if (context.direction === 'horizontal' && currentIndex > 0) {
                    event.preventDefault();
                    targetIndex = currentIndex - 1;
                }
                break;
            case 'ArrowRight':
                if (context.direction === 'horizontal' && currentIndex < items.length - 1) {
                    event.preventDefault();
                    targetIndex = currentIndex + 1;
                }
                break;
            case ' ':
            case 'Enter':
                event.preventDefault();
                // Trigger drag start
                (_a = context.onSortDragStart) === null || _a === void 0 ? void 0 : _a.call(context, { id, index: currentIndex, data: value }, event);
                return;
            case 'Escape':
                event.preventDefault();
                (_b = itemRef.current) === null || _b === void 0 ? void 0 : _b.blur();
                return;
            default:
                return;
        }
        if (targetIndex !== currentIndex) {
            context.onReorder(currentIndex, targetIndex);
            // Focus the item at new position after reorder
            setTimeout(() => {
                const targetItem = items[targetIndex];
                if (targetItem) {
                    targetItem[1].focus();
                }
            }, 100);
        }
    }, [disabled, context, id, value]);
    const handleDragStart = useCallback(() => {
        var _a;
        if (disabled)
            return;
        setIsDragging(true);
        context.setDraggedItem(id);
        context.setDraggedItemContent(children);
        const items = Array.from(context.itemRefs.current.entries());
        const currentIndex = items.findIndex(([itemId]) => itemId === id);
        (_a = context.onSortDragStart) === null || _a === void 0 ? void 0 : _a.call(context, { id, index: currentIndex, data: value });
    }, [disabled, id, children, context, value]);
    const handleDragEnd = useCallback(() => {
        var _a;
        setIsDragging(false);
        context.setDraggedItem(null);
        context.setDraggedItemContent(null);
        context.setDropPosition(null);
        const items = Array.from(context.itemRefs.current.entries());
        const currentIndex = items.findIndex(([itemId]) => itemId === id);
        (_a = context.onSortDragEnd) === null || _a === void 0 ? void 0 : _a.call(context, { id, index: currentIndex, data: value });
    }, [id, context, value]);
    const handlePointerDown = useCallback((event) => {
        if (disabled) {
            event.preventDefault();
            return;
        }
        // Only allow drag from handle if showDragHandle is true
        if (context.showDragHandle) {
            // Prevent drag from starting on the item itself
            event.preventDefault();
        }
    }, [disabled, context.showDragHandle]);
    const handleDragHandlePointerDown = useCallback((event) => {
        if (disabled) {
            event.preventDefault();
            return;
        }
        dragControls.start(event);
    }, [disabled, dragControls]);
    const isBeingDragged = context.draggedItem === id;
    return (jsxs(Reorder.Item, { ref: (node) => {
            itemRef.current = node;
            if (typeof forwardedRef === 'function') {
                forwardedRef(node);
            }
            else if (forwardedRef) {
                forwardedRef.current = node;
            }
        }, value: value || id, id: id, dragListener: !context.showDragHandle && !disabled, dragControls: context.showDragHandle ? dragControls : undefined, onDragStart: handleDragStart, onDragEnd: handleDragEnd, onPointerDown: handlePointerDown, tabIndex: disabled ? -1 : 0, onKeyDown: handleKeyDown, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), className: cn(theme.itemStyle, context.showDragHandle && 'flex items-center gap-2', disabled && 'opacity-50 cursor-not-allowed', !disabled && !context.showDragHandle && 'cursor-grab active:cursor-grabbing', isBeingDragged && 'shadow-lg z-50', isFocused && !disabled && 'ring-2 ring-blue-500 ring-offset-2', className), style: {
            position: 'relative',
        }, variants: sortableAnimations.variants, animate: isBeingDragged ? 'dragging' : 'idle', transition: sortableAnimations.layout.layout, children: [context.showDragHandle && (jsx(motion.div, { onPointerDown: handleDragHandlePointerDown, className: cn(theme.dragHandleStyle, disabled && 'cursor-not-allowed opacity-60', !disabled && 'cursor-grab active:cursor-grabbing'), whileHover: !disabled ? sortableAnimations.dragHandle.hover : undefined, whileTap: !disabled ? sortableAnimations.dragHandle.tap : undefined, children: disabled ? (jsx(LockClosedIcon, { className: "w-4 h-4 text-gray-500" })) : (jsx(Bars3Icon, { className: "w-4 h-4" })) })), jsx("div", { className: "flex-1", children: children })] }));
});
SortableItem.displayName = 'SortableItem';

export { Sortable, SortableItem };
