/**
 * SuggestionsProvider
 *
 * Provides suggestions state (query, suggestions, products, recent, trending, tabs),
 * actions (selectSuggestion, selectProduct, submitSearch, etc.), and keyboard navigation.
 * Uses useQuerySuggestionsEnhanced and useSuggestionsAnalytics; must be inside SearchProvider.
 */
import React, { useState, useCallback, useMemo, useEffect, useRef, } from "react";
import { useSearchContext } from "../SearchProvider";
import { useQuerySuggestionsEnhanced } from "../../hooks/useQuerySuggestionsEnhanced";
import { useSuggestionsAnalytics } from "../../hooks/useSuggestionsAnalytics";
import { SuggestionsContext } from "./SuggestionsContext";
export function SuggestionsProvider({ children, minQueryLength = 1, debounceMs = 200, maxSuggestions = 10, includeDropdownRecommendations = true, filteredTabs, enableAnalytics = true, analyticsTags, onSearch, onSuggestionSelect, onProductClick, storageKey, onStorageError, initialOpen = false, documentFieldMapping, }) {
    const searchCtx = useSearchContext();
    const client = searchCtx.client;
    const [query, setQueryState] = useState("");
    const [isOpen, setIsOpenState] = useState(initialOpen);
    // Sync query from parent SearchProvider's stateManager (driven by
    // stateManager.setQuery from the theme input bridge). Without this,
    // typing in the theme input only updates SearchProvider's query, not
    // SuggestionsProvider's.
    useEffect(() => {
        const sm = searchCtx.stateManager;
        if (!sm || typeof sm.subscribe !== "function")
            return;
        const unsub = sm.subscribe((state) => {
            const q = state?.query ?? "";
            setQueryState((prev) => (q !== prev ? q : prev));
        });
        return unsub;
    }, [searchCtx.stateManager]);
    const [activeIndex, setActiveIndex] = useState(-1);
    const [activeTabId, setActiveTabId] = useState("");
    const [prefetchedProducts, setPrefetchedProducts] = useState([]);
    // Prefetch product results only for predictive surface (initialOpen=true)
    // so the dropdown has products ready instantly. Other surfaces (search page)
    // already have results from their own search — no need for extra prefetch.
    const prefetchedProductsRef = useRef(false);
    useEffect(() => {
        if (!initialOpen || prefetchedProductsRef.current || !client?.search)
            return;
        prefetchedProductsRef.current = true;
        client
            .search("*", { per_page: 12, page: 1 })
            .then((res) => {
            const data = res?.data;
            const items = data?.results || data?.data || res?.results || res?.hits || [];
            if (Array.isArray(items) && items.length > 0) {
                setPrefetchedProducts(items);
            }
        })
            .catch(() => { });
    }, [client]);
    // Fix 5 (SCRUM-268): Open the suggestions panel when the theme input gains
    // focus via the `seekora:focus` custom DOM event dispatched by v4.js on the
    // predictive host element. This sets isOpen=true so the suggestions hook's
    // `enabled: isOpen` guard allows API fetches to fire. Without this, the
    // panel only showed recent searches (localStorage, always available) and
    // never fetched live suggestions/trending/categories/products.
    useEffect(() => {
        function handleFocusOpen() {
            setIsOpenState(true);
        }
        // Listen on document — v4.js dispatches the event on the host div which is
        // body-portalled; bubbling is disabled so we listen directly on document.
        // Use capture to intercept before any stopPropagation on the host.
        document.addEventListener("seekora:focus", handleFocusOpen, true);
        return () => {
            document.removeEventListener("seekora:focus", handleFocusOpen, true);
        };
    }, []);
    const suggestionsData = useQuerySuggestionsEnhanced({
        client,
        query,
        enabled: isOpen,
        prefetch: minQueryLength === 0,
        minQueryLength,
        debounceMs,
        maxSuggestions,
        includeDropdownRecommendations,
        filteredTabs,
        includeCategories: true,
        enableRecentSearches: true,
        maxRecentSearches: 8,
        analyticsTags,
        storageKey,
        onStorageError,
        documentFieldMapping,
    });
    const analytics = useSuggestionsAnalytics({
        client,
        enabled: enableAnalytics,
        analyticsTags,
        trackClicks: true,
        trackImpressions: true,
    });
    const { suggestions, recentSearches, trendingSearches, trendingProducts, filteredTabs: filteredTabsData, loading, error, hasContent, getAllNavigableItems, addRecentSearch, clearRecentSearches, } = suggestionsData;
    // Wrap getAllNavigableItems to include prefetchedProducts when trendingProducts is empty
    const getAllNavigableItemsWithPrefetched = useCallback(() => {
        const items = getAllNavigableItems();
        // If there are already product items in the list, return as-is
        if (items.some((i) => i.type === "product"))
            return items;
        // Otherwise append prefetchedProducts as navigable product items
        if (prefetchedProducts.length > 0) {
            let idx = items.length;
            prefetchedProducts.forEach((product) => {
                items.push({
                    type: "product",
                    index: idx++,
                    data: product,
                });
            });
        }
        return items;
    }, [getAllNavigableItems, prefetchedProducts]);
    const onSearchRef = useRef(onSearch);
    const onSuggestionSelectRef = useRef(onSuggestionSelect);
    const onProductClickRef = useRef(onProductClick);
    onSearchRef.current = onSearch;
    onSuggestionSelectRef.current = onSuggestionSelect;
    onProductClickRef.current = onProductClick;
    const setQuery = useCallback((q) => {
        setQueryState(q);
        setActiveIndex(-1);
    }, []);
    const close = useCallback(() => {
        analytics.trackDropdownClose(query);
        setIsOpenState(false);
        setActiveIndex(-1);
    }, [analytics, query]);
    const setIsOpen = useCallback((open) => {
        if (open) {
            setIsOpenState(true);
            analytics.trackDropdownOpen(query);
        }
        else {
            close();
        }
    }, [analytics, query, close]);
    const selectSuggestion = useCallback((suggestion, position) => {
        analytics.trackSuggestionClick({
            suggestion,
            position,
            query,
            totalSuggestions: suggestions.length,
        });
        setQuery(suggestion.query);
        // Reverse-sync to stateManager so the theme input updates
        if (searchCtx.stateManager?.setQuery) {
            searchCtx.stateManager.setQuery(suggestion.query, false);
        }
        onSuggestionSelectRef.current?.(suggestion);
        analytics.trackSearchSubmit(suggestion.query, true, suggestion);
        addRecentSearch(suggestion.query);
        if (!initialOpen)
            close();
        onSearchRef.current?.(suggestion.query);
    }, [analytics, query, suggestions.length, addRecentSearch, close]);
    const selectProduct = useCallback((product, position, section, tabId) => {
        analytics.trackProductClick({
            product,
            position,
            section: section ?? "products",
            tabId,
            query,
        });
        close();
        onProductClickRef.current?.(product);
    }, [analytics, query, close]);
    const selectRecentSearch = useCallback((search) => {
        analytics.trackRecentSearchClick(search);
        setQuery(search.query);
        close();
        onSearchRef.current?.(search.query);
        analytics.trackSearchSubmit(search.query, false);
    }, [analytics, close]);
    const selectTrendingSearch = useCallback((trending, position) => {
        analytics.trackTrendingClick(trending, position);
        setQuery(trending.query);
        close();
        onSearchRef.current?.(trending.query);
        analytics.trackSearchSubmit(trending.query, false);
    }, [analytics, close]);
    const setActiveTab = useCallback((tab) => {
        setActiveTabId(tab.id);
        analytics.trackTabSelect(tab, query);
    }, [analytics, query]);
    const submitSearch = useCallback((q, fromSuggestion, suggestion) => {
        const trimmed = q.trim();
        if (!trimmed)
            return;
        analytics.trackSearchSubmit(trimmed, !!fromSuggestion, suggestion);
        addRecentSearch(trimmed);
        close();
        onSearchRef.current?.(trimmed);
    }, [analytics, addRecentSearch, close]);
    const navigateNext = useCallback(() => {
        const items = getAllNavigableItemsWithPrefetched();
        if (items.length === 0)
            return;
        setActiveIndex((i) => (i < items.length - 1 ? i + 1 : 0));
    }, [getAllNavigableItemsWithPrefetched]);
    const navigatePrev = useCallback(() => {
        const items = getAllNavigableItemsWithPrefetched();
        if (items.length === 0)
            return;
        setActiveIndex((i) => (i <= 0 ? items.length - 1 : i - 1));
    }, [getAllNavigableItemsWithPrefetched]);
    const selectActive = useCallback(() => {
        const items = getAllNavigableItemsWithPrefetched();
        if (activeIndex < 0 || activeIndex >= items.length) {
            submitSearch(query);
            return;
        }
        const item = items[activeIndex];
        switch (item.type) {
            case "suggestion":
                selectSuggestion(item.data, item.index);
                break;
            case "product":
                selectProduct(item.data, item.index, "products");
                break;
            case "recent":
                selectRecentSearch(item.data);
                break;
            case "trending":
                selectTrendingSearch(item.data, item.index);
                break;
            default:
                submitSearch(query);
        }
    }, [
        activeIndex,
        getAllNavigableItemsWithPrefetched,
        query,
        submitSearch,
        selectSuggestion,
        selectProduct,
        selectRecentSearch,
        selectTrendingSearch,
    ]);
    // Impression when open and content changes
    useEffect(() => {
        if (!isOpen || !hasContent || !query)
            return;
        analytics.trackImpression({
            suggestions,
            products: trendingProducts,
            query,
            timestamp: Date.now(),
        });
    }, [isOpen, hasContent, query, suggestions, trendingProducts, analytics]);
    const value = useMemo(() => ({
        query,
        setQuery,
        isOpen,
        setIsOpen,
        activeIndex,
        setActiveIndex,
        suggestions,
        recentSearches,
        trendingSearches,
        trendingProducts,
        prefetchedProducts,
        filteredTabs: filteredTabsData,
        activeTabId,
        setActiveTabId,
        loading,
        error,
        hasContent,
        getAllNavigableItems: getAllNavigableItemsWithPrefetched,
        selectSuggestion,
        selectProduct,
        selectRecentSearch,
        selectTrendingSearch,
        setActiveTab,
        submitSearch,
        clearRecentSearches,
        close,
        navigateNext,
        navigatePrev,
        selectActive,
    }), [
        query,
        setQuery,
        isOpen,
        setIsOpen,
        activeIndex,
        setActiveIndex,
        suggestions,
        recentSearches,
        trendingSearches,
        trendingProducts,
        prefetchedProducts,
        filteredTabsData,
        activeTabId,
        setActiveTabId,
        loading,
        error,
        hasContent,
        getAllNavigableItemsWithPrefetched,
        selectSuggestion,
        selectProduct,
        selectRecentSearch,
        selectTrendingSearch,
        setActiveTab,
        submitSearch,
        clearRecentSearches,
        close,
        navigateNext,
        navigatePrev,
        selectActive,
    ]);
    return (React.createElement(SuggestionsContext.Provider, { value: value }, children));
}
