import { jsx as _jsx } from "react/jsx-runtime";
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
import { toLibrariesTreeItem } from '../LibrariesSidebar/utils';
const EMPTY_ITEMS = [];
const SidebarContext = createContext(undefined);
/**
 * Access the sidebar context. Throws if used outside {@link SidebarProvider},
 * so consumers never have to handle an `undefined` value.
 */
export const useSidebarContext = () => {
    const context = useContext(SidebarContext);
    if (!context) {
        throw new Error('useSidebarContext must be used within SidebarProvider');
    }
    return context;
};
/**
 * Single owner of all sidebar state, exposed to every row/header through one
 * memoized context value. Responsibilities:
 *
 * - **Item index** — builds `itemsById` (O(1) lookup via `getTreeItem`) and
 *   `childrenByParent` from the flat `items` prop, both memoized on `items`.
 * - **Sort mode** — `isSortMode` toggle and `showSortButton` (on when `onSort`
 *   is provided).
 * - **Pending order** — while sorting, drag results are staged per parent in
 *   `pendingSortByParent`; `getOrderedItems` returns the pending order when set,
 *   else the natural order. On Save the order is emitted via `onSort` and kept
 *   applied until the refetched `items` reflect it (an effect clears the staged
 *   order whenever `items` change outside sort mode).
 * - **Row expansion** — tracked in a `Set` ref (`expandedIds`) so toggling a
 *   row never re-renders the tree and the open/closed state survives remounts.
 * - **Consumer callbacks** — forwards `onItemClick` / `onAddMouseEnter`.
 *
 * @param model - Normalized sidebar nodes from the consumer adapter layer.
 * @param capabilities - Presentation flags (create, sort, status badge).
 * @param onSort - Called on Save with the reordered items grouped by parent.
 * @param onItemClick - Called when a row is clicked (outside sort mode).
 * @param onAddMouseEnter - Called when hovering an add button (e.g. to prefetch).
 */
export const SidebarProvider = ({ model, capabilities, children, onSort, onItemClick, onAddMouseEnter, }) => {
    const items = useMemo(() => model.nodes.map(node => toLibrariesTreeItem(node, capabilities)), [model.nodes, capabilities]);
    const [isSortMode, setIsSortMode] = useState(false);
    const [pendingSortByParent, setPendingSortByParent] = useState(() => new Map());
    const expandedIdsRef = useRef(new Set());
    const isSortModeRef = useRef(isSortMode);
    isSortModeRef.current = isSortMode;
    const nodesSignature = useMemo(() => model.nodes.map(node => `${node.id}:${node.position}`).join(','), [model.nodes]);
    const prevNodesSignatureRef = useRef(nodesSignature);
    useEffect(() => {
        if (prevNodesSignatureRef.current === nodesSignature)
            return;
        prevNodesSignatureRef.current = nodesSignature;
        if (!isSortModeRef.current)
            setPendingSortByParent(new Map());
    }, [nodesSignature]);
    const { itemsById, childrenByParent } = useMemo(() => {
        const byId = new Map();
        const byParent = new Map();
        for (const item of items) {
            byId.set(item.id, item);
            const key = item.parentId ?? null;
            const siblings = byParent.get(key);
            if (siblings)
                siblings.push(item);
            else
                byParent.set(key, [item]);
        }
        return { itemsById: byId, childrenByParent: byParent };
    }, [items]);
    const getTreeItem = useCallback((id) => itemsById.get(id), [itemsById]);
    const getOrderedItems = useCallback((parentId) => {
        const key = parentId ?? null;
        const pending = pendingSortByParent.get(key);
        if (pending !== undefined)
            return pending;
        return childrenByParent.get(key) ?? EMPTY_ITEMS;
    }, [pendingSortByParent, childrenByParent]);
    const setPendingSort = useCallback((nextItems, parentId) => {
        const key = parentId ?? null;
        setPendingSortByParent(prev => {
            const next = new Map(prev);
            next.set(key, nextItems);
            return next;
        });
    }, []);
    const setRowExpanded = useCallback((id, expanded) => {
        if (expanded)
            expandedIdsRef.current.add(id);
        else
            expandedIdsRef.current.delete(id);
    }, []);
    const handleRestoreSortableItems = useCallback(() => setPendingSortByParent(new Map()), []);
    const handleToggleSortMode = useCallback(() => setIsSortMode(prev => !prev), []);
    const handleSaveSort = useCallback(() => {
        if (onSort && pendingSortByParent.size > 0) {
            const newItems = Array.from(pendingSortByParent.entries()).map(([key, sortedItems]) => ({
                items: sortedItems,
                parentId: typeof key === 'number' ? key : null,
            }));
            onSort(newItems);
        }
        // Keep the pending order applied until the refetched `items` reflect it
        // (cleared by the effect above); only leave sort mode here.
        setIsSortMode(false);
    }, [onSort, pendingSortByParent]);
    const value = useMemo(() => ({
        getTreeItem,
        onItemClick,
        onAddMouseEnter,
        isSortMode,
        showSortButton: Boolean(onSort),
        getOrderedItems,
        setPendingSort,
        handleToggleSortMode,
        handleRestoreSortableItems,
        handleSaveSort,
        expandedIds: expandedIdsRef.current,
        setRowExpanded,
    }), [
        getTreeItem,
        onItemClick,
        onAddMouseEnter,
        isSortMode,
        onSort,
        getOrderedItems,
        setPendingSort,
        handleToggleSortMode,
        handleRestoreSortableItems,
        handleSaveSort,
        setRowExpanded,
    ]);
    return (_jsx(SidebarContext.Provider, { value: value, children: children }));
};
