// ThemeHarvestPredictiveList — renders Seekora predictive-search product
// suggestions using the merchant's own harvested predictive row HTML (SCRUM-267).
//
// Analogous to ThemeHarvestGrid but for the dropdown surface:
//   - Self-sources the query product results from the SuggestionsContext
//     (the active tab's products), like ProductGrid does — it is a layout
//     primitive, so nothing passes a `suggestions` prop.
//   - Renders a <ul role="listbox"> containing per-product <li> rows.
//   - Each row clones the harvested predictive-card template via swapCardHtml.
//   - form is always null on predictive swap maps (rows are link-only).
//   - No IntersectionObserver — predictive rows are transient (closed on blur).
//   - Delegated click on the container fires Object Clicked with surface "dropdown".
//   - Fallback row rendered when swap throws (XSS-safe DOM construction).
//
// Harvest / fallback behavior:
//   - cardTemplateHtml + swapMap present  → harvested <ul> of themed <li> rows.
//   - cardTemplateHtml or swapMap absent  → <ProductGrid> fallback (generic-theme /
//     harvest-unavailable stores); columns and asLink props are forwarded.
//   - Harvest available but no products   → null (nothing to show).
import React, { useCallback, useMemo } from "react";
import { clsx } from "clsx";
import { useObjectClickedBuffer } from "../hooks/useObjectClickedBuffer";
import { useSuggestionsContext } from "./suggestions-primitives/SuggestionsContext";
import { toHydrogenProduct } from "../theme-cards/adapter";
import { swapCardHtml } from "../theme-cards/swap";
import { ProductGrid } from "./suggestions-primitives/ProductGrid";
/**
 * XSS-safe fallback row for when swapCardHtml throws on a product.
 * Uses DOM construction (no string concat) so handle/title values
 * containing `"` or `<` cannot inject markup or break attribute syntax.
 */
function fallbackRowHtml(item, index) {
    try {
        const doc = item?.document || item || {};
        const title = String(doc.title || doc.name || `Result ${index + 1}`);
        const handle = String(doc.handle || "");
        const li = document.createElement("li");
        li.className = "seekora-predictive-fallback";
        const a = document.createElement("a");
        a.href = handle ? `/products/${handle}` : "#";
        a.textContent = title;
        li.appendChild(a);
        return li.outerHTML;
    }
    catch {
        return `<li class="seekora-predictive-fallback"><a href="#">Product</a></li>`;
    }
}
export const ThemeHarvestPredictiveList = ({ source = "trending", maxItems = 6, suggestions, cardTemplateHtml, swapMap, moneyFormat, searchId, onResultClick, className, listClassName, columns = 3, asLink = true, }) => {
    const clickBuffer = useObjectClickedBuffer();
    const { trendingProducts, filteredTabs, activeTabId } = useSuggestionsContext();
    // Self-source the products from context (same pattern as ProductGrid), unless
    // an explicit `suggestions` array is passed. "tab" = the active query tab's
    // product matches; that's the predictive product-results list.
    const items = useMemo(() => {
        if (suggestions)
            return suggestions;
        if (source === "trending")
            return trendingProducts ?? [];
        const tab = (filteredTabs ?? []).find((t) => t.id === (source === "tab" ? activeTabId : source));
        return tab?.products ?? [];
    }, [suggestions, source, trendingProducts, filteredTabs, activeTabId]);
    const limited = useMemo(() => items.slice(0, maxItems), [items, maxItems]);
    // Pre-compute swapped HTML for all rows. Memoized so unrelated parent
    // re-renders (e.g. input cursor moves) do not re-clone the template N times.
    const swappedHtmls = useMemo(() => limited.map((item, i) => {
        try {
            const product = toHydrogenProduct(item);
            return swapCardHtml(cardTemplateHtml, swapMap, product, moneyFormat);
        }
        catch {
            return fallbackRowHtml(item, i);
        }
    }), [limited, cardTemplateHtml, swapMap, moneyFormat]);
    const handleContainerClick = useCallback((e) => {
        const target = e.target;
        const row = target.closest("[data-seekora-theme-card]");
        if (!row)
            return;
        const idx = Number(row.dataset.seekoraIndex);
        const item = limited[idx];
        if (!item)
            return;
        if (searchId) {
            clickBuffer.record(String(item?.id ?? item?.document?.id ?? idx), idx + 1, searchId, {
                object_type: "product",
                ...(item?.document?.title || item?.title
                    ? { name: item?.document?.title || item?.title }
                    : {}),
            }, "dropdown");
        }
        if (onResultClick)
            onResultClick(item, idx);
    }, [limited, clickBuffer, searchId, onResultClick]);
    if (!cardTemplateHtml || !swapMap) {
        return (React.createElement(ProductGrid, { source: source, maxItems: maxItems, columns: columns, asLink: asLink, className: className }));
    }
    if (limited.length === 0)
        return null;
    return (React.createElement("ul", { role: "listbox", className: clsx(className, listClassName), onClick: handleContainerClick }, swappedHtmls.map((html, i) => (React.createElement("li", { key: i, role: "option", "aria-selected": false, "data-seekora-theme-card": "", "data-seekora-index": i, 
        // eslint-disable-next-line react/no-danger
        dangerouslySetInnerHTML: { __html: html } })))));
};
