/**
 * @fileoverview Debounce utilities to replace manual timer management.
 *
 * Two variants:
 * - `Debouncer` — single debounced operation (replaces timer + clearTimeout pattern)
 * - `KeyedDebouncer` — per-key debouncing (replaces Map<string, Timeout> pattern)
 *
 * Both integrate with CleanupManager via dispose().
 *
 * @module utils/debouncer
 */
/**
 * Single-operation debouncer.
 *
 * Replaces the common pattern of:
 * ```
 * private timer: NodeJS.Timeout | null = null;
 * debounce(fn) { if (this.timer) clearTimeout(this.timer); this.timer = setTimeout(fn, delay); }
 * cancel() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } }
 * ```
 *
 * @example
 * ```typescript
 * private saveDeb = new Debouncer(500);
 *
 * onChange() {
 *   this.saveDeb.schedule(() => this.save());
 * }
 *
 * stop() {
 *   this.saveDeb.dispose();
 * }
 * ```
 */
export class Debouncer {
    delayMs;
    timer = null;
    constructor(delayMs) {
        this.delayMs = delayMs;
    }
    /**
     * Schedule a debounced callback. Resets the timer on each call.
     * If a previous call is pending, it is cancelled.
     */
    schedule(fn) {
        this.cancel();
        this.timer = setTimeout(() => {
            this.timer = null;
            fn();
        }, this.delayMs);
    }
    /** Cancel any pending execution without invoking the callback. */
    cancel() {
        if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
    }
    /** Whether a callback is currently pending. */
    get isPending() {
        return this.timer !== null;
    }
    /**
     * Cancel pending callback and flush immediately.
     * Useful for shutdown: cancel the timer but run the action now.
     *
     * @param fn - The flush function to run (typically the same function passed to schedule)
     */
    flush(fn) {
        this.cancel();
        fn();
    }
    /** Alias for cancel() — matches CleanupManager/Disposable convention. */
    dispose() {
        this.cancel();
    }
}
/**
 * Per-key debouncer for operations that need independent timers per resource.
 *
 * Replaces the common pattern of:
 * ```
 * private timers = new Map<string, NodeJS.Timeout>();
 * debounce(key, fn) {
 *   const existing = this.timers.get(key);
 *   if (existing) clearTimeout(existing);
 *   this.timers.set(key, setTimeout(() => { this.timers.delete(key); fn(); }, delay));
 * }
 * ```
 *
 * @example
 * ```typescript
 * private fileDebouncers = new KeyedDebouncer(100);
 *
 * onFileChange(path: string) {
 *   this.fileDebouncers.schedule(path, () => this.processFile(path));
 * }
 *
 * stop() {
 *   this.fileDebouncers.dispose();
 * }
 * ```
 */
export class KeyedDebouncer {
    delayMs;
    timers = new Map();
    constructor(delayMs) {
        this.delayMs = delayMs;
    }
    /**
     * Schedule a debounced callback for a specific key.
     * Each key has its own independent timer.
     */
    schedule(key, fn) {
        this.cancelKey(key);
        this.timers.set(key, setTimeout(() => {
            this.timers.delete(key);
            fn();
        }, this.delayMs));
    }
    /** Cancel a pending callback for a specific key. */
    cancelKey(key) {
        const existing = this.timers.get(key);
        if (existing) {
            clearTimeout(existing);
            this.timers.delete(key);
        }
    }
    /** Whether a callback is pending for a specific key. */
    has(key) {
        return this.timers.has(key);
    }
    /** Number of active timers. */
    get size() {
        return this.timers.size;
    }
    /** Get all currently active keys. */
    keys() {
        return this.timers.keys();
    }
    /** Cancel all pending callbacks. */
    dispose() {
        for (const timer of this.timers.values()) {
            clearTimeout(timer);
        }
        this.timers.clear();
    }
    /**
     * Cancel all pending callbacks and run a flush function for each active key.
     * Useful for shutdown: cancel timers but run the action for each pending key.
     *
     * @param fn - Called once per active key with the key as argument
     */
    flushAll(fn) {
        const activeKeys = Array.from(this.timers.keys());
        this.dispose();
        for (const key of activeKeys) {
            fn(key);
        }
    }
}
//# sourceMappingURL=debouncer.js.map