/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
/**
 * A utility that manages a timeout and an AbortController, allowing the
 * timeout to be paused, resumed, and dynamically extended.
 */
export class DeadlineTimer {
    controller;
    timeoutId = null;
    remainingMs;
    lastStartedAt;
    isPaused = false;
    constructor(timeoutMs, reason = 'Timeout exceeded.') {
        this.controller = new AbortController();
        this.remainingMs = timeoutMs;
        this.lastStartedAt = Date.now();
        this.schedule(timeoutMs, reason);
    }
    /** The AbortSignal managed by this timer. */
    get signal() {
        return this.controller.signal;
    }
    /**
     * Pauses the timer, clearing any active timeout.
     */
    pause() {
        if (this.isPaused || this.controller.signal.aborted)
            return;
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
        const elapsed = Date.now() - this.lastStartedAt;
        this.remainingMs = Math.max(0, this.remainingMs - elapsed);
        this.isPaused = true;
    }
    /**
     * Resumes the timer with the remaining budget.
     */
    resume(reason = 'Timeout exceeded.') {
        if (!this.isPaused || this.controller.signal.aborted)
            return;
        this.lastStartedAt = Date.now();
        this.schedule(this.remainingMs, reason);
        this.isPaused = false;
    }
    /**
     * Extends the current budget by the specified number of milliseconds.
     */
    extend(ms, reason = 'Timeout exceeded.') {
        if (this.controller.signal.aborted)
            return;
        if (this.isPaused) {
            this.remainingMs += ms;
        }
        else {
            if (this.timeoutId) {
                clearTimeout(this.timeoutId);
            }
            const elapsed = Date.now() - this.lastStartedAt;
            this.remainingMs = Math.max(0, this.remainingMs - elapsed) + ms;
            this.lastStartedAt = Date.now();
            this.schedule(this.remainingMs, reason);
        }
    }
    /**
     * Aborts the signal immediately and clears any pending timers.
     */
    abort(reason) {
        if (this.timeoutId) {
            clearTimeout(this.timeoutId);
            this.timeoutId = null;
        }
        this.isPaused = false;
        this.controller.abort(reason);
    }
    schedule(ms, reason) {
        this.timeoutId = setTimeout(() => {
            this.timeoutId = null;
            this.controller.abort(new Error(reason));
        }, ms);
    }
}
//# sourceMappingURL=deadlineTimer.js.map