import { anySignal } from 'any-signal';
import { setMaxListeners } from 'main-event';
import { MovingAverage } from './moving-average.js';
export const DEFAULT_TIMEOUT_MULTIPLIER = 1.2;
export const DEFAULT_FAILURE_MULTIPLIER = 2;
export const DEFAULT_MIN_TIMEOUT = 5_000;
export const DEFAULT_MAX_TIMEOUT = 60_000;
export const DEFAULT_INTERVAL = 5_000;
export class AdaptiveTimeout {
    success;
    failure;
    next;
    metric;
    timeoutMultiplier;
    failureMultiplier;
    minTimeout;
    maxTimeout;
    constructor(init = {}) {
        const interval = init.interval ?? DEFAULT_INTERVAL;
        this.success = new MovingAverage(interval);
        this.failure = new MovingAverage(interval);
        this.next = new MovingAverage(interval);
        this.failureMultiplier = init.failureMultiplier ?? DEFAULT_FAILURE_MULTIPLIER;
        this.timeoutMultiplier = init.timeoutMultiplier ?? DEFAULT_TIMEOUT_MULTIPLIER;
        this.minTimeout = init.minTimeout ?? DEFAULT_MIN_TIMEOUT;
        this.maxTimeout = init.maxTimeout ?? DEFAULT_MAX_TIMEOUT;
        if (init.metricName != null) {
            this.metric = init.metrics?.registerMetricGroup(init.metricName);
        }
    }
    getTimeoutSignal(options = {}) {
        // calculate timeout for individual peers based on moving average of
        // previous successful requests
        let timeout = Math.round(this.next.movingAverage * (options.timeoutFactor ?? this.timeoutMultiplier));
        if (timeout < this.minTimeout) {
            timeout = this.minTimeout;
        }
        if (timeout > this.maxTimeout) {
            timeout = this.maxTimeout;
        }
        const sendTimeout = AbortSignal.timeout(timeout);
        const timeoutSignal = anySignal([options.signal, sendTimeout]);
        setMaxListeners(Infinity, timeoutSignal, sendTimeout);
        timeoutSignal.start = Date.now();
        timeoutSignal.timeout = timeout;
        return timeoutSignal;
    }
    cleanUp(signal) {
        const time = Date.now() - signal.start;
        if (signal.aborted) {
            this.failure.push(time);
            this.next.push(time * this.failureMultiplier);
            this.metric?.update({
                failureMovingAverage: this.failure.movingAverage,
                failureDeviation: this.failure.deviation,
                failureForecast: this.failure.forecast,
                failureVariance: this.failure.variance,
                failure: time
            });
        }
        else {
            this.success.push(time);
            this.next.push(time);
            this.metric?.update({
                successMovingAverage: this.success.movingAverage,
                successDeviation: this.success.deviation,
                successForecast: this.success.forecast,
                successVariance: this.success.variance,
                success: time
            });
        }
    }
}
//# sourceMappingURL=adaptive-timeout.js.map