/**
 * BufferProvider — React lifecycle shell for the V4 batched-event buffers.
 *
 * Phase 2b of the analytics V4 pivot (SCRUM-259). The accumulation /
 * dedupe / flush machinery lives in
 * `@seekora-ai/ui-sdk-core/analytics/bufferFactory` so it can be unit
 * tested without React and reused by future vue/vanilla wrappers. This
 * file is the React-specific shell:
 *
 *   - threads the SearchProvider's TrackContext into the factory,
 *   - registers `beforeunload`/`pagehide` listeners so navigation-time
 *     clicks survive,
 *   - exposes the three buffers via React context to descendants.
 *
 * Three buffers are exposed:
 *   - impression  — `Search Impression` (debounced ~100 ms).
 *   - click       — `Object Clicked` + `Object Clicked Outside Search`
 *                   (idle-timeout ~1000 ms OR unload OR explicit flush).
 *   - viewed      — `Object Viewed` (debounced ~250 ms).
 *
 * Usage (consumers):
 *   const buffer = useSearchImpressionBuffer();
 *   buffer.record(objectID, position, search_id, objectData?, surface?);
 *
 *   const click = useObjectClickedBuffer();
 *   click.record(objectID, position, search_id, objectData?, surface?);
 *   click.recordOutsideSearch(objectID, position?, objectData?, surface?);
 *
 *   const view = useObjectViewedBuffer();
 *   view.record(objectID, position?, objectData?, surface?);
 */
import React, { createContext, useContext, useEffect, useMemo, useRef, } from "react";
import { createBufferTrio, } from "@seekora-ai/ui-sdk-core";
import { useTrackContext } from "../SearchProvider";
export { createBufferTrio } from "@seekora-ai/ui-sdk-core";
const BufferContext = createContext(null);
/**
 * Wraps descendants with the three V4 batched-event buffers.
 *
 * `SearchResults` / recommendation widgets / PDP related-rails should be
 * descendants. Cards inside those subtrees pick up the buffers via the
 * `useSearchImpressionBuffer` / `useObjectClickedBuffer` /
 * `useObjectViewedBuffer` accessor hooks.
 *
 * Without a `<SearchProvider>` above (or without a TrackContext on it)
 * every buffer becomes a no-op and silently drops `record()` calls.
 */
export const BufferProvider = ({ children, ctxOverride, config, }) => {
    const providerCtx = useTrackContext();
    const ctx = ctxOverride !== undefined ? ctxOverride : providerCtx;
    // Stable buffer trio across renders. The factory captures ctx in a
    // closure; in App Router (and SSR-first frameworks generally) the very
    // first render happens with `ctx === null` because window is undefined
    // and SeekoraProviders' analytics-next instance can't be created. The
    // hydration pass then provides a real ctx — we propagate it through
    // setCtx() so the closure-captured value tracks the live one. Without
    // this, clicks fired after hydration kept hitting `if (!ctx) return`
    // inside flushClicks and were silently discarded (SCRUM-272). We keep
    // a single trio instance for the lifetime of the provider so buffered
    // entries survive re-renders.
    const trioRef = useRef(null);
    if (trioRef.current === null) {
        trioRef.current = createBufferTrio(ctx, config);
    }
    const trio = trioRef.current;
    // Push every ctx flip into the trio so the captured closure sees the
    // latest TrackContext. `setCtx(null)` is fine — the buffer record/flush
    // paths handle it the same way as the initial null state.
    useEffect(() => {
        trio.setCtx(ctx);
    }, [trio, ctx]);
    // beforeunload guard — drain everything on navigation so clicks made
    // moments before the user leaves don't get dropped.
    useEffect(() => {
        if (typeof window === "undefined")
            return undefined;
        const onUnload = () => {
            try {
                trio.flushAll();
            }
            catch {
                /* swallow */
            }
        };
        window.addEventListener("beforeunload", onUnload);
        window.addEventListener("pagehide", onUnload);
        return () => {
            window.removeEventListener("beforeunload", onUnload);
            window.removeEventListener("pagehide", onUnload);
        };
    }, [trio]);
    // Tear down outstanding timers on unmount (or StrictMode double-mount).
    useEffect(() => {
        return () => {
            trio.__teardown();
        };
    }, [trio]);
    // Expose only the public surface via context (strip __teardown).
    const value = useMemo(() => ({
        impression: trio.impression,
        click: trio.click,
        viewed: trio.viewed,
        flushAll: trio.flushAll,
        setCtx: trio.setCtx,
    }), [trio]);
    return (React.createElement(BufferContext.Provider, { value: value }, children));
};
/** Pull the full buffer trio (used by integration tests + advanced callers). */
export function useBufferTrio() {
    return useContext(BufferContext);
}
