/**
 * SearchBarWithSuggestions Component
 *
 * Integrated search bar with query suggestions dropdown.
 * Combines SearchBar with any of the suggestion dropdown variants.
 *
 * ## Usage mode
 *
 * Full input + external dropdown + analytics. Always renders inside a SearchProvider.
 * Pair with a variant ('classic' | 'rich' | 'federated' | 'compact') to control
 * the suggestions dropdown style.
 *
 * ```tsx
 * <SearchProvider client={client}>
 *   <SearchBarWithSuggestions
 *     variant="federated"
 *     onSearch={(q) => navigate('/search?q=' + q)}
 *   />
 * </SearchProvider>
 * ```
 *
 * For a purely standalone input with no provider dependency, use SearchInput instead.
 */
import React, { useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle, } from "react";
import { useSearchContext } from "./SearchProvider";
import { QuerySuggestionsDropdown, } from "./QuerySuggestionsDropdown";
import { RichQuerySuggestions, } from "./RichQuerySuggestions";
import { FederatedDropdown } from "./FederatedDropdown";
import { useSuggestionsAnalytics } from "../hooks/useSuggestionsAnalytics";
import { clsx } from "clsx";
import { ensureSeekoraStyles } from "../styles/inject";
// ============================================================================
// Styles
// ============================================================================
const styles = {
    wrapper: {
        position: "relative",
        width: "100%",
    },
    inputWrapper: {
        display: "flex",
        alignItems: "center",
        backgroundColor: "var(--seekora-bg-surface, #ffffff)",
        border: "1px solid var(--seekora-border-color, #e5e7eb)",
        borderRadius: "var(--seekora-border-radius-lg, 8px)",
        overflow: "hidden",
        transition: "border-color 150ms ease, box-shadow 150ms ease",
    },
    inputWrapperFocused: {
        borderColor: "var(--seekora-border-focus, var(--seekora-primary, #3b82f6))",
        boxShadow: "0 0 0 3px var(--seekora-border-focus-alpha, var(--seekora-primary-light, rgba(59, 130, 246, 0.1)))",
    },
    input: {
        flex: 1,
        padding: "12px 16px",
        fontSize: "16px",
        border: "none",
        outline: "none",
        backgroundColor: "transparent",
        color: "var(--seekora-text-primary, #111827)",
        fontFamily: "inherit",
    },
    searchIcon: {
        width: "20px",
        height: "20px",
        color: "var(--seekora-text-secondary, #9ca3af)",
        marginLeft: "12px",
        flexShrink: 0,
    },
    clearButton: {
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        width: "32px",
        height: "32px",
        marginRight: "4px",
        padding: 0,
        border: "none",
        backgroundColor: "transparent",
        color: "var(--seekora-text-secondary, #9ca3af)",
        borderRadius: "50%",
        cursor: "pointer",
        transition: "background-color 150ms ease, color 150ms ease",
    },
    searchButton: {
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        gap: "8px",
        padding: "12px 20px",
        margin: "4px",
        border: "none",
        backgroundColor: "var(--seekora-primary, #3b82f6)",
        color: "var(--seekora-primary-text, white)",
        borderRadius: "var(--seekora-border-radius, 6px)",
        fontSize: "14px",
        fontWeight: 600,
        cursor: "pointer",
        transition: "background-color 150ms ease",
    },
};
// ============================================================================
// Icons
// ============================================================================
const SearchIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", className: "seekora-search-bar-with-suggestions__search-icon" },
    React.createElement("path", { fillRule: "evenodd", d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", clipRule: "evenodd" })));
const ClearIcon = () => (React.createElement("svg", { viewBox: "0 0 20 20", fill: "currentColor", className: "seekora-search-bar-with-suggestions__clear-icon" },
    React.createElement("path", { fillRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z", clipRule: "evenodd" })));
// ============================================================================
// Component
// ============================================================================
export const SearchBarWithSuggestions = forwardRef(function SearchBarWithSuggestions(props, ref) {
    const { variant = "classic", placeholder = "Powered by Seekora", initialQuery = "", value, onQueryChange, onSearch, onSuggestionSelect, onProductClick, showSearchButton = false, searchButtonText = "Search", showClearButton = true, autoFocus = false, minQueryLength = 1, maxSuggestions = 8, maxProducts = 8, debounceMs = 200, showRecentSearches = true, showTrendingOnEmpty = true, includeDropdownRecommendations = false, filteredTabs, enableAnalytics = true, analyticsTags, includeFacets, includeCategories, dropdownWidth, dropdownMaxHeight, classNames = {}, style, inputStyle, ariaLabel = "Search", submitMode = "spa", searchResultsUrl = "/search?q=", } = props;
    const { client } = useSearchContext();
    const inputRef = useRef(null);
    const containerRef = useRef(null);
    const dropdownContainerRef = useRef(null);
    const dropdownRef = useRef(null);
    const [query, setQuery] = useState(value ?? initialQuery);
    const [isFocused, setIsFocused] = useState(false);
    const [isDropdownOpen, setIsDropdownOpen] = useState(false);
    // In-flight latch — prevents rapid-fire clicks from issuing parallel searches.
    // Shared across recent-search, suggestion-select, and product-click handlers.
    const searchLatchRef = useRef(false);
    // Analytics — declared before the pointer-down effect that references it to
    // avoid a TDZ error (the dep array evaluates at render time).
    const analytics = useSuggestionsAnalytics({
        client,
        enabled: enableAnalytics,
        analyticsTags,
    });
    useEffect(() => {
        ensureSeekoraStyles();
    }, []);
    // Defect #26 — pointer-down-outside to dismiss dropdown (no setTimeout race)
    useEffect(() => {
        if (!isDropdownOpen)
            return;
        const handlePointerDown = (e) => {
            const target = e.target;
            if (containerRef.current?.contains(target) ||
                dropdownContainerRef.current?.contains(target)) {
                return;
            }
            setIsDropdownOpen(false);
            setIsFocused(false);
            analytics.trackDropdownClose(query);
        };
        document.addEventListener("pointerdown", handlePointerDown);
        document.addEventListener("mousedown", handlePointerDown);
        return () => {
            document.removeEventListener("pointerdown", handlePointerDown);
            document.removeEventListener("mousedown", handlePointerDown);
        };
    }, [isDropdownOpen, query, analytics]);
    // Sync controlled value
    useEffect(() => {
        if (value !== undefined && value !== query) {
            setQuery(value);
        }
    }, [value]);
    // Handle query change
    const handleQueryChange = useCallback((e) => {
        const newQuery = e.target.value;
        setQuery(newQuery);
        onQueryChange?.(newQuery);
        // Open dropdown when typing — but only if there is content to show or a
        // fetch is in flight (prevents an empty dropdown when minQueryLength=0 and
        // no trending/recent data has loaded yet).
        if (newQuery.length >= minQueryLength ||
            (showTrendingOnEmpty && newQuery.length === 0)) {
            const itemCount = dropdownRef.current?.getTotalItems?.() ?? 0;
            const hasContent = itemCount > 0;
            // Allow open when there is content. Also allow when the dropdown is
            // already open (user is mid-session and items may be transitioning).
            if (hasContent || isDropdownOpen) {
                setIsDropdownOpen(true);
            }
        }
    }, [onQueryChange, minQueryLength, showTrendingOnEmpty, isDropdownOpen]);
    // Handle search submit — Defect #3: respects submitMode
    const handleSubmit = useCallback((e) => {
        if (submitMode === "spa") {
            // Always prevent ancestor form submission in spa mode
            e?.preventDefault();
        }
        if (query.trim()) {
            onSearch?.(query.trim());
            analytics.trackSearchSubmit(query.trim(), false);
            setIsDropdownOpen(false);
            if (submitMode === "navigate" &&
                e &&
                "type" in e &&
                e.type !== "submit") {
                // Keyboard Enter in navigate mode with no ancestor form — fallback navigation
                const ancestorForm = inputRef.current?.closest("form");
                if (!ancestorForm) {
                    window.location.href =
                        searchResultsUrl + encodeURIComponent(query.trim());
                }
            }
        }
    }, [query, onSearch, analytics, submitMode, searchResultsUrl]);
    // Handle suggestion select
    const handleSuggestionSelect = useCallback((suggestion) => {
        if (searchLatchRef.current)
            return;
        searchLatchRef.current = true;
        setQuery(suggestion.query);
        onQueryChange?.(suggestion.query);
        onSuggestionSelect?.(suggestion);
        analytics.trackSuggestionClick({
            suggestion,
            position: 0, // Would need to track actual position
            query,
        });
        setIsDropdownOpen(false);
        // Trigger search and release latch when it settles
        const maybePromise = onSearch?.(suggestion.query);
        if (maybePromise && typeof maybePromise.then === "function") {
            maybePromise.finally(() => {
                searchLatchRef.current = false;
            });
        }
        else {
            requestAnimationFrame(() => {
                searchLatchRef.current = false;
            });
        }
    }, [query, onQueryChange, onSuggestionSelect, onSearch, analytics]);
    // Handle product click — latch prevents rapid-click from firing parallel navigations
    const handleProductClick = useCallback((product) => {
        if (searchLatchRef.current)
            return;
        searchLatchRef.current = true;
        analytics.trackProductClick({
            product,
            position: 0,
            query,
        });
        setIsDropdownOpen(false);
        const maybePromise = onProductClick?.(product);
        if (maybePromise && typeof maybePromise.then === "function") {
            maybePromise.finally(() => {
                searchLatchRef.current = false;
            });
        }
        else {
            requestAnimationFrame(() => {
                searchLatchRef.current = false;
            });
        }
    }, [query, onProductClick, analytics]);
    // Handle recent search click — latch prevents rapid-click from firing parallel searches
    const handleRecentSearchClick = useCallback((search) => {
        if (searchLatchRef.current)
            return;
        searchLatchRef.current = true;
        setQuery(search.query);
        onQueryChange?.(search.query);
        analytics.trackRecentSearchClick(search);
        setIsDropdownOpen(false);
        const maybePromise = onSearch?.(search.query);
        if (maybePromise && typeof maybePromise.then === "function") {
            maybePromise.finally(() => {
                searchLatchRef.current = false;
            });
        }
        else {
            requestAnimationFrame(() => {
                searchLatchRef.current = false;
            });
        }
    }, [onQueryChange, onSearch, analytics]);
    // Handle focus
    const handleFocus = useCallback(() => {
        setIsFocused(true);
        setIsDropdownOpen(true);
        analytics.trackDropdownOpen(query);
    }, [query, analytics]);
    // Handle blur — Defect #26: no setTimeout; pointer-down-outside effect handles
    // click-outside. This only handles keyboard Tab-away.
    const handleBlur = useCallback((e) => {
        const relatedTarget = e.relatedTarget;
        if (relatedTarget &&
            (containerRef.current?.contains(relatedTarget) ||
                dropdownContainerRef.current?.contains(relatedTarget))) {
            // Focus stayed inside — e.g. user clicked clear button
            return;
        }
        setIsFocused(false);
        setIsDropdownOpen(false);
        analytics.trackDropdownClose(query);
    }, [query, analytics]);
    // Handle clear
    const handleClear = useCallback(() => {
        setQuery("");
        onQueryChange?.("");
        inputRef.current?.focus();
    }, [onQueryChange]);
    // Handle keyboard navigation
    const handleKeyDown = useCallback((e) => {
        if (!isDropdownOpen) {
            if (e.key === "ArrowDown" || e.key === "ArrowUp") {
                setIsDropdownOpen(true);
            }
            return;
        }
        switch (e.key) {
            case "ArrowDown":
                e.preventDefault();
                // Guard: do not navigate into an empty dropdown (0 items → crash)
                if ((dropdownRef.current?.getTotalItems?.() ?? 0) > 0) {
                    dropdownRef.current?.navigateNext?.();
                }
                break;
            case "ArrowUp":
                e.preventDefault();
                if ((dropdownRef.current?.getTotalItems?.() ?? 0) > 0) {
                    dropdownRef.current?.navigatePrevious?.();
                }
                break;
            case "Enter":
                if ((dropdownRef.current?.getActiveIndex?.() ?? -1) >= 0) {
                    // Extra guard: ensure the dropdown actually has items before selecting
                    if ((dropdownRef.current?.getTotalItems?.() ?? 0) > 0) {
                        e.preventDefault();
                        dropdownRef.current?.selectActive?.();
                    }
                }
                else {
                    // Pass the keyboard event so handleSubmit can respect submitMode
                    handleSubmit(e);
                }
                break;
            case "Escape":
                e.preventDefault();
                setIsDropdownOpen(false);
                inputRef.current?.blur();
                break;
        }
    }, [isDropdownOpen, handleSubmit]);
    // Expose ref methods
    useImperativeHandle(ref, () => ({
        focus: () => inputRef.current?.focus(),
        blur: () => inputRef.current?.blur(),
        clear: handleClear,
        getQuery: () => query,
        setQuery: (newQuery) => {
            setQuery(newQuery);
            onQueryChange?.(newQuery);
        },
        openDropdown: () => setIsDropdownOpen(true),
        closeDropdown: () => setIsDropdownOpen(false),
        navigateNext: () => dropdownRef.current?.navigateNext?.(),
        navigatePrevious: () => dropdownRef.current?.navigatePrevious?.(),
        selectCurrent: () => dropdownRef.current?.selectActive?.(),
    }), [query, handleClear, onQueryChange]);
    // Render dropdown based on variant
    const renderDropdown = () => {
        const commonProps = {
            query,
            isOpen: isDropdownOpen,
            maxSuggestions,
            minQueryLength,
            debounceMs,
            showRecentSearches,
            classNames,
            onSuggestionSelect: handleSuggestionSelect,
            onRecentSearchClick: handleRecentSearchClick,
            onClose: () => setIsDropdownOpen(false),
            analyticsTags,
        };
        switch (variant) {
            case "rich":
                return (React.createElement(RichQuerySuggestions, { ref: dropdownRef, ...commonProps, includeDropdownRecommendations: includeDropdownRecommendations, includeCategories: true, width: dropdownWidth || "100%", maxHeight: dropdownMaxHeight || "480px" }));
            case "federated":
                return (React.createElement(FederatedDropdown, { ref: dropdownRef, ...commonProps, maxProducts: maxProducts, filteredTabs: filteredTabs, showProducts: true, showBrands: true, showFilteredTabs: !!filteredTabs, onProductClick: handleProductClick, width: dropdownWidth || "800px", maxHeight: dropdownMaxHeight || "600px", includeFacets: includeFacets, includeCategories: includeCategories, includeDropdownRecommendations: includeDropdownRecommendations }));
            case "compact":
                return (React.createElement(QuerySuggestionsDropdown, { ref: dropdownRef, ...commonProps, maxSuggestions: 5, showCounts: false, width: dropdownWidth || "100%" }));
            case "classic":
            default:
                return (React.createElement(QuerySuggestionsDropdown, { ref: dropdownRef, ...commonProps, width: dropdownWidth || "100%" }));
        }
    };
    return (React.createElement("div", { className: clsx("seekora-search-bar-with-suggestions", classNames.wrapper), style: style },
        React.createElement("div", { role: "search", ref: containerRef },
            React.createElement("div", { className: clsx("seekora-search-bar-with-suggestions__input-wrapper", isFocused &&
                    "seekora-search-bar-with-suggestions__input-wrapper--focused") },
                React.createElement(SearchIcon, null),
                React.createElement("input", { ref: inputRef, type: "text", value: query, onChange: handleQueryChange, onFocus: handleFocus, onBlur: handleBlur, onKeyDown: handleKeyDown, placeholder: placeholder, autoFocus: autoFocus, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false, "aria-label": ariaLabel, "aria-expanded": isDropdownOpen, "aria-haspopup": "listbox", "aria-autocomplete": "list", role: "combobox", className: clsx("seekora-search-bar-with-suggestions__input", classNames.input), style: inputStyle }),
                showClearButton && query && (React.createElement("button", { type: "button", onClick: handleClear, className: clsx("seekora-search-bar-with-suggestions__clear-btn", classNames.clearButton), "aria-label": "Clear search" },
                    React.createElement(ClearIcon, null))),
                showSearchButton && (React.createElement("button", { type: "button", onClick: () => handleSubmit(), className: clsx("seekora-search-bar-with-suggestions__search-btn", classNames.button) }, searchButtonText)))),
        React.createElement("div", { ref: dropdownContainerRef, className: clsx("seekora-search-bar-with-suggestions__dropdown", classNames.dropdown) }, renderDropdown())));
});
export default SearchBarWithSuggestions;
