"use client";
/**
 * React behavior hook for the execution waterfall timeline.
 *
 * Derives waterfall entries from the event stream, manages live-growing
 * bars via `requestAnimationFrame` during streaming, and provides a
 * stable `scrollToTask` callback for graph-timeline synchronization.
 *
 * Follows DD-003 (headless-first) and DD-010 (referential stability).
 *
 * @since T07 (Execution Waterfall Timeline)
 */
import { useEffect, useMemo, useRef, useState } from "react";
import { deriveWaterfallEntries, deriveWaterfallScale, } from "./derive-waterfall-entries.js";
// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------
const EMPTY_ENTRIES = [];
const EMPTY_SCALE = { totalMs: 1000, ticks: [0, 1000], labelEveryN: 1 };
export function useWaterfallEntries({ events, streamState, executionStartIso, executionDurationMs, }) {
    const isLive = streamState.stage === "streaming" || streamState.stage === "reconnecting";
    const execStartEpoch = useMemo(() => (executionStartIso ? new Date(executionStartIso).getTime() : 0), [executionStartIso]);
    // --- Live clock (rAF-driven nowMs for growing bars) ---
    const [nowMs, setNowMs] = useState(() => execStartEpoch > 0 ? Date.now() - execStartEpoch : 0);
    const rafRef = useRef(0);
    useEffect(() => {
        if (!isLive || execStartEpoch === 0) {
            if (rafRef.current)
                cancelAnimationFrame(rafRef.current);
            return;
        }
        let mounted = true;
        const tick = () => {
            if (!mounted)
                return;
            setNowMs(Date.now() - execStartEpoch);
            rafRef.current = requestAnimationFrame(tick);
        };
        rafRef.current = requestAnimationFrame(tick);
        return () => {
            mounted = false;
            if (rafRef.current)
                cancelAnimationFrame(rafRef.current);
        };
    }, [isLive, execStartEpoch]);
    // When execution completes, snap to final duration
    useEffect(() => {
        if (!isLive && executionDurationMs != null && executionDurationMs > 0) {
            setNowMs(executionDurationMs);
        }
    }, [isLive, executionDurationMs]);
    // --- Derive entries (memoized on event list length) ---
    const entries = useMemo(() => {
        if (events.length === 0)
            return EMPTY_ENTRIES;
        return deriveWaterfallEntries(events, executionStartIso);
    }, [events, executionStartIso]);
    // --- Derive scale ---
    const effectiveDuration = isLive ? nowMs : (executionDurationMs ?? nowMs);
    const scale = useMemo(() => {
        if (entries.length === 0)
            return EMPTY_SCALE;
        return deriveWaterfallScale(entries, effectiveDuration);
    }, [entries, effectiveDuration]);
    return useMemo(() => ({ entries, scale, isLive, nowMs }), [entries, scale, isLive, nowMs]);
}
//# sourceMappingURL=useWaterfallEntries.js.map