/**
 * FormDataAuditor — invisible component that diffs `form_data` between
 * renders and records each changed field as a `manual` audit entry.
 *
 * Coalescing strategy: controlled inputs fire on every keystroke, which
 * would produce one audit entry per character. Instead, we debounce per
 * field — when a field changes we (re)start a per-field timer, and once
 * the user has been idle for `debounce_ms` we record ONE entry whose
 * `prev_value` is the last committed value and whose `new_value` is the
 * latest current value. The result is one entry per typing burst.
 *
 * On unmount any pending timers are flushed so we don't lose unfinished
 * edits. Internal keys prefixed with `__` (file attachments,
 * clarification storage, etc.) are ignored.
 *
 * Renders nothing.
 */
'use client';
import { useEffect, useRef } from 'react';
import { useFieldAuditOptional } from './context.js';
export function FormDataAuditor({ form_data, current_user, debounce_ms = 750, }) {
    const audit = useFieldAuditOptional();
    // Last committed values — what we'll use as `prev_value` when the next
    // debounced entry fires. Starts at the initial form_data so we don't
    // emit a synthetic "everything changed" entry on first mount.
    const committed_ref = useRef({ ...(form_data ?? {}) });
    // Live form_data, kept fresh via render so the debounced timer reads
    // the latest value at fire time (not the value at the time we set the timer).
    const live_ref = useRef(form_data ?? {});
    live_ref.current = form_data ?? {};
    // Per-field debounce state. `current_user` is captured into the closure.
    const pending_ref = useRef(new Map());
    // Diff each render and schedule audit entries.
    useEffect(() => {
        if (!audit)
            return;
        const prev = committed_ref.current;
        const curr = form_data ?? {};
        const pending = pending_ref.current;
        const keys = new Set([...Object.keys(prev), ...Object.keys(curr)]);
        for (const key of keys) {
            if (key.startsWith('__'))
                continue;
            const baseline = pending.get(key)?.baseline ?? prev[key];
            const after = curr[key];
            // If the value is back at the baseline (e.g. user typed then erased),
            // cancel any pending entry and forget the field.
            if (Object.is(baseline, after)) {
                const existing = pending.get(key);
                if (existing) {
                    clearTimeout(existing.timer);
                    pending.delete(key);
                }
                continue;
            }
            // Restart the per-field debounce.
            const existing = pending.get(key);
            if (existing) {
                clearTimeout(existing.timer);
            }
            const timer = setTimeout(() => {
                const latest = live_ref.current[key];
                const prev_value = pending.get(key)?.baseline ?? prev[key];
                pending.delete(key);
                if (Object.is(prev_value, latest))
                    return;
                audit.record_change({
                    field_id: key,
                    source: 'manual',
                    prev_value,
                    new_value: latest,
                    user: current_user,
                });
                committed_ref.current = { ...committed_ref.current, [key]: latest };
            }, debounce_ms);
            pending.set(key, { baseline, timer });
        }
    }, [form_data, audit, current_user, debounce_ms]);
    // Flush any pending timers on unmount.
    useEffect(() => {
        return () => {
            const pending = pending_ref.current;
            for (const [, entry] of pending) {
                clearTimeout(entry.timer);
            }
            pending.clear();
        };
    }, []);
    return null;
}
//# sourceMappingURL=auditor.js.map