import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
// (C) 2024-2025 GoodData Corporation
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
import { useVirtualizer } from "@tanstack/react-virtual";
import { forwardRefWithGenerics } from "@gooddata/sdk-ui";
import { bem } from "../@utils/bem.js";
import { makeLinearKeyboardNavigation } from "../@utils/keyboardNavigation.js";
import { UiSkeleton } from "../UiSkeleton/UiSkeleton.js";
const { b, e } = bem("gd-ui-kit-paged-virtual-list");
function UiPagedVirtualListNotWrapped(props, ref) {
    const { SkeletonItem = UiSkeleton, items, itemHeight, itemsGap, itemPadding, tabIndex = 0, onKeyDownSelect, closeDropdown, customKeyboardNavigationHandler, children, scrollbarHoverEffect, } = props;
    const { itemsCount, scrollContainerRef, height, hasScroll, rowVirtualizer, virtualItems } = useVirtualList(props);
    useImperativeHandle(ref, () => ({
        scrollToItem: (item) => {
            const itemIndex = items?.findIndex((i) => i === item) ?? -1;
            if (itemIndex >= 0) {
                rowVirtualizer.scrollToIndex(itemIndex, {
                    align: "center",
                    behavior: "smooth",
                });
            }
        },
    }));
    const { focusedIndex, onKeyboardNavigation } = useVirtualListKeyboardNavigation(items, onKeyDownSelect, closeDropdown);
    return (_jsx("div", { className: b({
            hasScroll,
        }), tabIndex: tabIndex, onKeyDown: tabIndex < 0 ? undefined : (customKeyboardNavigationHandler ?? onKeyboardNavigation), children: _jsx("div", { ref: scrollContainerRef, className: e("scroll-container", { hover: scrollbarHoverEffect }), style: { height, paddingTop: itemsGap }, children: _jsx("div", { style: {
                    height: `${rowVirtualizer.getTotalSize()}px`,
                    width: "100%",
                    position: "relative",
                }, children: virtualItems.map((virtualRow) => {
                    const item = items?.[virtualRow.index];
                    const isSkeletonItem = virtualRow.index > itemsCount - 1;
                    const style = {
                        width: `calc(100% + ${hasScroll ? "10px" : "0px"})`,
                        height: `${virtualRow.size}px`,
                        transform: `translateY(${virtualRow.start}px)`,
                        paddingRight: itemPadding,
                        paddingLeft: itemPadding,
                    };
                    return (_jsxs("div", { className: e("item", { isFocused: focusedIndex === virtualRow.index }), style: style, children: [isSkeletonItem ? (_jsx(SkeletonItem, { itemHeight: itemHeight }, virtualRow.index)) : (children(item)), _jsx("div", { className: e("gap"), style: { height: itemsGap } })] }, virtualRow.index));
                }) }) }) }));
}
/**
 * @internal
 */
export const UiPagedVirtualList = forwardRefWithGenerics(UiPagedVirtualListNotWrapped);
function useVirtualList(props) {
    const { items, itemHeight, itemsGap, skeletonItemsCount, hasNextPage, loadNextPage, isLoading, maxHeight, scrollToItem, scrollToItemKeyExtractor, scrollToIndex, shouldLoadNextPage: shouldLoadNextPageProps, } = props;
    const defaultShouldLoadNextPage = useCallback((lastItemIndex, itemsCount, skeletonItemsCount) => lastItemIndex >= itemsCount - 1 - skeletonItemsCount, []);
    const shouldLoadNextPage = useMemo(() => {
        return shouldLoadNextPageProps ?? defaultShouldLoadNextPage;
    }, [shouldLoadNextPageProps, defaultShouldLoadNextPage]);
    const scrollContainerRef = useRef(null);
    const itemsCount = items ? items.length : 0;
    let renderItemsCount = itemsCount;
    if (hasNextPage) {
        renderItemsCount = itemsCount + skeletonItemsCount;
    }
    else if (itemsCount === 0 && isLoading) {
        renderItemsCount = skeletonItemsCount;
    }
    //
    const realHeight = itemsCount > 0
        ? (itemHeight + itemsGap) * itemsCount + itemsGap
        : skeletonItemsCount * (itemHeight + itemsGap) + itemsGap;
    const height = Math.min(maxHeight, realHeight);
    const hasScroll = scrollContainerRef.current
        ? scrollContainerRef.current?.scrollHeight >
            scrollContainerRef.current?.getBoundingClientRect().height
        : false;
    const rowVirtualizer = useVirtualizer({
        count: renderItemsCount,
        getScrollElement: () => scrollContainerRef.current,
        estimateSize: () => itemHeight + itemsGap,
        overscan: 5,
    });
    const virtualItems = rowVirtualizer.getVirtualItems();
    useEffect(() => {
        const [lastItem] = [...virtualItems].reverse();
        if (!lastItem) {
            return;
        }
        if (shouldLoadNextPage(lastItem.index, itemsCount, skeletonItemsCount) && hasNextPage && !isLoading) {
            loadNextPage?.();
        }
    }, [
        hasNextPage,
        loadNextPage,
        itemsCount,
        isLoading,
        virtualItems,
        skeletonItemsCount,
        shouldLoadNextPage,
    ]);
    // Add scroll into view effect
    useEffect(() => {
        if (!scrollToItem || !items || items.length === 0) {
            return;
        }
        const findItemIndex = () => {
            if (scrollToItemKeyExtractor) {
                const targetKey = scrollToItemKeyExtractor(scrollToItem);
                return items.findIndex((item) => scrollToItemKeyExtractor(item) === targetKey);
            }
            else {
                return items.findIndex((item) => item === scrollToItem);
            }
        };
        const index = findItemIndex();
        if (index === -1) {
            return;
        }
        rowVirtualizer.scrollToIndex(index, {
            align: "center",
            behavior: "smooth",
        });
    }, [scrollToItem, items, rowVirtualizer, itemHeight, itemsGap, scrollToItemKeyExtractor]);
    useEffect(() => {
        if (scrollToIndex !== undefined) {
            rowVirtualizer.scrollToIndex(scrollToIndex, {
                align: "center",
                behavior: "smooth",
            });
        }
    }, [scrollToIndex, rowVirtualizer]);
    return {
        itemsCount,
        scrollContainerRef,
        height,
        hasScroll,
        rowVirtualizer,
        virtualItems,
    };
}
function useVirtualListKeyboardNavigation(items, onKeyDownSelect, closeDropdown) {
    const [focusedIndex, setFocusedIndex] = useState(0);
    useEffect(() => {
        setFocusedIndex(0);
    }, [items]);
    const virtualListKeyboardNavigationHandler = useMemo(() => makeLinearKeyboardNavigation({
        onFocusNext: () => {
            setFocusedIndex((prevIndex) => {
                const nextIndex = prevIndex + 1;
                if (nextIndex >= items.length) {
                    return 0;
                }
                return nextIndex;
            });
        },
        onFocusPrevious: () => {
            setFocusedIndex((prevIndex) => {
                const nextIndex = prevIndex - 1;
                if (nextIndex < 0) {
                    return items.length - 1;
                }
                return nextIndex;
            });
        },
        onFocusFirst: () => {
            setFocusedIndex(0);
        },
        onFocusLast: () => {
            setFocusedIndex(items.length - 1);
        },
        onSelect: () => {
            onKeyDownSelect?.(items[focusedIndex]);
            closeDropdown?.();
        },
        onClose: (e) => {
            e.preventDefault();
            closeDropdown?.();
        },
    }), [items, focusedIndex, onKeyDownSelect, closeDropdown]);
    return {
        focusedIndex,
        onKeyboardNavigation: virtualListKeyboardNavigationHandler,
    };
}
//# sourceMappingURL=UiPagedVirtualList.js.map