/**
 * useCardAnalytics — V4 batched-event wiring for product/item card primitives.
 *
 * Bundles the impression + click events every product/item card should
 * emit. The pair of events fired depends on the card's `surface`:
 *
 *   * `"results_page"` / `"dropdown"` — search-attributed. Records the
 *     card's viewport entry into the `SearchImpressionBuffer` and its
 *     click into the `ObjectClickedBuffer` (search-attributed bucket).
 *     The buffers flush ONE batched event with parallel arrays per
 *     `(search_id, surface)` bucket — never per card.
 *   * `"recommendation_widget"` / `"outside_search"` — not search-attributed.
 *     Records visibility into the `ObjectViewedBuffer` and clicks into
 *     the outside-search bucket of `ObjectClickedBuffer`. Flushes ONE
 *     `Object Viewed` and ONE `Object Clicked Outside Search` per
 *     `(surface)` bucket.
 *
 * Returns a `ref` to attach to the card root + a wrapped click handler.
 * Consumer components (ProductCard, ItemCard, etc.) keep their own
 * markup; this hook owns the tracker plumbing so we don't repeat it.
 *
 * Requires a `<BufferProvider>` above (which `SearchResults` and other
 * consumers wrap automatically). If no provider is above, the buffer
 * hooks return no-op shims so descendant cards still render and click
 * handlers still navigate — the only thing that goes silent is the V4
 * analytics emission.
 *
 * Phase 2b — SCRUM-259 V4 pivot (replaces the per-card emission model).
 */
import { useCallback, useEffect, useRef } from "react";
import { observeOnce } from "@seekora-ai/ui-sdk-core";
import { useTrackContext } from "../components/SearchProvider";
import { useSearchImpressionBuffer } from "./useSearchImpressionBuffer";
import { useObjectClickedBuffer } from "./useObjectClickedBuffer";
import { useObjectViewedBuffer } from "./useObjectViewedBuffer";
const SEARCH_ATTRIBUTED_SURFACES = new Set(["results_page", "dropdown"]);
export function useCardAnalytics(props) {
    const { objectId, position, objectType = "product", surface = "results_page", searchId, objectData, onSelect, enabled = true, } = props;
    // Track-context presence drives whether the buffers will actually emit.
    // (The buffer hooks themselves are null-safe; this is a fast-path
    // check that lets us avoid wiring the IntersectionObserver entirely
    // when there's nobody listening.)
    const trackCtx = useTrackContext();
    const impressionBuffer = useSearchImpressionBuffer();
    const clickBuffer = useObjectClickedBuffer();
    const viewedBuffer = useObjectViewedBuffer();
    const ref = useRef(null);
    const isSearchAttributed = SEARCH_ATTRIBUTED_SURFACES.has(surface);
    // Wire the IntersectionObserver. On first viewport entry, record the
    // entry into the right buffer (impression for search-attributed; view
    // for outside-search). The observer fires exactly once per element
    // via `observeOnce`.
    useEffect(() => {
        if (!enabled)
            return undefined;
        const el = ref.current;
        if (!el || !trackCtx)
            return undefined;
        return observeOnce(el, () => {
            if (isSearchAttributed) {
                if (!searchId)
                    return; // no search_id, no Search Impression
                impressionBuffer.record(objectId, position, searchId, buildObjectData(objectData, objectType), surface);
            }
            else {
                viewedBuffer.record(objectId, position, buildObjectData(objectData, objectType), surface);
            }
        });
        // The buffer references are stable across renders (useMemo on
        // useBufferTrio output), so they don't churn this effect.
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [
        enabled,
        trackCtx,
        isSearchAttributed,
        objectId,
        position,
        surface,
        searchId,
        objectType,
    ]);
    const onClick = useCallback(() => {
        if (enabled && trackCtx) {
            try {
                const data = buildObjectData(objectData, objectType);
                if (isSearchAttributed) {
                    if (searchId) {
                        clickBuffer.record(objectId, position, searchId, data, surface);
                    }
                }
                else {
                    clickBuffer.recordOutsideSearch(objectId, position, data, surface);
                }
            }
            catch {
                // Never let analytics break the click flow.
            }
        }
        if (onSelect)
            onSelect();
    }, [
        enabled,
        trackCtx,
        isSearchAttributed,
        objectId,
        position,
        surface,
        searchId,
        objectType,
        objectData,
        onSelect,
        clickBuffer,
    ]);
    return { ref, onClick };
}
/**
 * Merge optional `objectData` with the `object_type` discriminator so the
 * downstream JSON column carries the type for funnel queries. Returns
 * `undefined` (not an empty object) when there's literally nothing to
 * thread — that lets the buffer drop the `objectData[]` array entirely
 * when no card supplies data.
 */
function buildObjectData(data, objectType) {
    if (!data && (!objectType || objectType === "product"))
        return undefined;
    return { object_type: objectType, ...(data ?? {}) };
}
