/**
 * Tool Throttle
 *
 * Throttles tool phase updates to prevent overwhelming
 * the UI with too many updates. Default throttle is 80ms.
 */
/**
 * Default throttle configuration
 */
export const DEFAULT_THROTTLE_CONFIG = {
    intervalMs: 80,
    leading: true,
    trailing: true,
    maxQueueSize: 100,
};
// ============================================================================
// Throttle Function
// ============================================================================
/**
 * Create a throttled function
 */
export function throttle(fn, config = {}) {
    const cfg = { ...DEFAULT_THROTTLE_CONFIG, ...config };
    let lastCall = 0;
    let timeoutId = null;
    let lastArgs = null;
    const throttled = function (...args) {
        const now = Date.now();
        const elapsed = now - lastCall;
        const execute = () => {
            lastCall = Date.now();
            lastArgs = null;
            fn.apply(this, args);
        };
        if (elapsed >= cfg.intervalMs) {
            // First call or interval has passed
            if (cfg.leading) {
                execute();
            }
            else {
                lastArgs = args;
                if (!timeoutId) {
                    timeoutId = setTimeout(() => {
                        timeoutId = null;
                        if (lastArgs) {
                            fn.apply(this, lastArgs);
                            lastArgs = null;
                            lastCall = Date.now();
                        }
                    }, cfg.intervalMs);
                }
            }
        }
        else {
            // Within interval, queue for trailing call
            lastArgs = args;
            if (cfg.trailing && !timeoutId) {
                timeoutId = setTimeout(() => {
                    timeoutId = null;
                    if (lastArgs) {
                        fn.apply(this, lastArgs);
                        lastArgs = null;
                        lastCall = Date.now();
                    }
                }, cfg.intervalMs - elapsed);
            }
        }
    };
    throttled.cancel = () => {
        if (timeoutId) {
            clearTimeout(timeoutId);
            timeoutId = null;
        }
        lastArgs = null;
    };
    throttled.flush = () => {
        if (timeoutId) {
            clearTimeout(timeoutId);
            timeoutId = null;
        }
        if (lastArgs) {
            fn.apply(null, lastArgs);
            lastArgs = null;
            lastCall = Date.now();
        }
    };
    return throttled;
}
/**
 * Throttles tool phase events by tool call ID
 */
export class ToolPhaseThrottler {
    config;
    queues = new Map();
    lastEmit = new Map();
    timers = new Map();
    callback = null;
    constructor(config = {}) {
        this.config = { ...DEFAULT_THROTTLE_CONFIG, ...config };
    }
    /**
     * Set the callback for throttled events
     */
    setCallback(callback) {
        this.callback = callback;
    }
    /**
     * Push an event to the throttler
     */
    push(event) {
        const { toolCallId, phase } = event;
        const now = Date.now();
        // Start and result phases are always emitted immediately
        if (phase === 'start' || phase === 'result') {
            this.emitEvent(event);
            this.clearQueue(toolCallId);
            return;
        }
        // Check if we can emit immediately
        const lastEmitTime = this.lastEmit.get(toolCallId) || 0;
        const elapsed = now - lastEmitTime;
        if (elapsed >= this.config.intervalMs) {
            // Emit immediately if interval has passed
            this.emitEvent(event);
            this.lastEmit.set(toolCallId, now);
        }
        else {
            // Queue for later emission
            this.queueEvent(toolCallId, event);
            // Schedule trailing emission if not already scheduled
            if (!this.timers.has(toolCallId)) {
                const timer = setTimeout(() => {
                    this.flushQueue(toolCallId);
                }, this.config.intervalMs - elapsed);
                this.timers.set(toolCallId, timer);
            }
        }
    }
    /**
     * Queue an event for later emission
     */
    queueEvent(toolCallId, event) {
        let queue = this.queues.get(toolCallId);
        if (!queue) {
            queue = [];
            this.queues.set(toolCallId, queue);
        }
        queue.push({ event, timestamp: Date.now() });
        // Trim queue if too large (keep most recent)
        if (queue.length > this.config.maxQueueSize) {
            queue.shift();
        }
    }
    /**
     * Flush queued events for a tool call
     */
    flushQueue(toolCallId) {
        const timer = this.timers.get(toolCallId);
        if (timer) {
            clearTimeout(timer);
            this.timers.delete(toolCallId);
        }
        const queue = this.queues.get(toolCallId);
        if (queue && queue.length > 0) {
            // Emit only the most recent event
            const latest = queue[queue.length - 1];
            this.emitEvent(latest.event);
            this.lastEmit.set(toolCallId, Date.now());
            this.queues.delete(toolCallId);
        }
    }
    /**
     * Clear queue for a tool call
     */
    clearQueue(toolCallId) {
        const timer = this.timers.get(toolCallId);
        if (timer) {
            clearTimeout(timer);
            this.timers.delete(toolCallId);
        }
        this.queues.delete(toolCallId);
        this.lastEmit.delete(toolCallId);
    }
    /**
     * Emit an event via callback
     */
    emitEvent(event) {
        if (this.callback) {
            try {
                this.callback(event);
            }
            catch {
                // Ignore callback errors
            }
        }
    }
    /**
     * Flush all pending events
     */
    flushAll() {
        for (const toolCallId of this.queues.keys()) {
            this.flushQueue(toolCallId);
        }
    }
    /**
     * Cancel all pending events
     */
    cancelAll() {
        for (const timer of this.timers.values()) {
            clearTimeout(timer);
        }
        this.timers.clear();
        this.queues.clear();
        this.lastEmit.clear();
    }
    /**
     * Get throttle interval
     */
    getInterval() {
        return this.config.intervalMs;
    }
    /**
     * Set throttle interval
     */
    setInterval(intervalMs) {
        this.config.intervalMs = intervalMs;
    }
    /**
     * Dispose
     */
    dispose() {
        this.cancelAll();
        this.callback = null;
    }
}
// ============================================================================
// Singleton
// ============================================================================
let throttlerInstance = null;
/**
 * Get or create the ToolPhaseThrottler singleton
 */
export function getToolPhaseThrottler(config) {
    if (!throttlerInstance) {
        throttlerInstance = new ToolPhaseThrottler(config);
    }
    return throttlerInstance;
}
/**
 * Reset the ToolPhaseThrottler singleton
 */
export function resetToolPhaseThrottler() {
    if (throttlerInstance) {
        throttlerInstance.dispose();
    }
    throttlerInstance = null;
}
//# sourceMappingURL=tool-throttle.js.map