/**
 * webhook/debounce.ts
 *
 * Time-window debouncing for content change webhooks.
 *
 * When multiple documents are edited in rapid succession (e.g., an editor
 * updating several related articles), we coalesce them into a single
 * evaluation dispatch. This prevents wasting evaluation budget on
 * intermediate states.
 *
 * Strategy: accumulate changed slugs in a time window (default: 5 minutes).
 * When the window expires, dispatch a single evaluation for all accumulated
 * areas. New edits during the window reset the timer ("trailing edge").
 *
 * @see docs/design-docs/report-store/visibility-workflows.md — Debouncing
 */
/**
 * Create a debouncer with a configurable time window.
 *
 * @param windowMs - Debounce window in milliseconds (default: 300_000 = 5 min)
 * @param onFlush - Callback invoked when the window closes
 */
export function createDebouncer(windowMs, onFlush) {
    const state = {
        slugs: new Set(),
        timer: null,
        windowStart: 0,
    };
    function resetTimer() {
        if (state.timer !== null) {
            clearTimeout(state.timer);
        }
        state.timer = setTimeout(() => {
            void doFlush();
        }, windowMs);
    }
    async function doFlush() {
        if (state.slugs.size === 0)
            return;
        const slugs = [...state.slugs];
        // Reset state before callback (so new pushes start a fresh window)
        state.slugs.clear();
        if (state.timer !== null) {
            clearTimeout(state.timer);
            state.timer = null;
        }
        state.windowStart = 0;
        await onFlush(slugs);
    }
    return {
        cancel() {
            if (state.timer !== null) {
                clearTimeout(state.timer);
                state.timer = null;
            }
            state.slugs.clear();
            state.windowStart = 0;
        },
        async flush() {
            await doFlush();
        },
        pending() {
            return state.slugs.size;
        },
        push(slug) {
            if (state.slugs.size === 0) {
                state.windowStart = Date.now();
            }
            state.slugs.add(slug);
            resetTimer();
        },
        state() {
            return { ...state, slugs: new Set(state.slugs) };
        },
    };
}
