"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AbortError = void 0;
exports.default = pRetry;
exports.makeRetriable = makeRetriable;
/* eslint-disable */
// @ts-nocheck
// p-retry code vendored to avoid import issues
// Source: https://github.com/sindresorhus/p-retry
const index_js_1 = __importDefault(require("../is-network-error/index.cjs"));
function validateRetries(retries) {
    if (typeof retries === "number") {
        if (retries < 0) {
            throw new TypeError("Expected `retries` to be a non-negative number.");
        }
        if (Number.isNaN(retries)) {
            throw new TypeError("Expected `retries` to be a valid number or Infinity, got NaN.");
        }
    }
    else if (retries !== undefined) {
        throw new TypeError("Expected `retries` to be a number or Infinity.");
    }
}
function validateNumberOption(name, value, { min = 0, allowInfinity = false } = {}) {
    if (value === undefined) {
        return;
    }
    if (typeof value !== "number" || Number.isNaN(value)) {
        throw new TypeError(`Expected \`${name}\` to be a number${allowInfinity ? " or Infinity" : ""}.`);
    }
    if (!allowInfinity && !Number.isFinite(value)) {
        throw new TypeError(`Expected \`${name}\` to be a finite number.`);
    }
    if (value < min) {
        throw new TypeError(`Expected \`${name}\` to be \u2265 ${min}.`);
    }
}
class AbortError extends Error {
    constructor(message) {
        super();
        if (message instanceof Error) {
            this.originalError = message;
            ({ message } = message);
        }
        else {
            this.originalError = new Error(message);
            this.originalError.stack = this.stack;
        }
        this.name = "AbortError";
        this.message = message;
    }
}
exports.AbortError = AbortError;
function calculateDelay(retriesConsumed, options) {
    const attempt = Math.max(1, retriesConsumed + 1);
    const random = options.randomize ? Math.random() + 1 : 1;
    let timeout = Math.round(random * options.minTimeout * options.factor ** (attempt - 1));
    timeout = Math.min(timeout, options.maxTimeout);
    return timeout;
}
function calculateRemainingTime(start, max) {
    if (!Number.isFinite(max)) {
        return max;
    }
    return max - (performance.now() - start);
}
async function onAttemptFailure({ error, attemptNumber, retriesConsumed, startTime, options, }) {
    const normalizedError = error instanceof Error
        ? error
        : new TypeError(`Non-error was thrown: "${error}". You should only throw errors.`);
    if (normalizedError instanceof AbortError) {
        throw normalizedError.originalError;
    }
    const retriesLeft = Number.isFinite(options.retries)
        ? Math.max(0, options.retries - retriesConsumed)
        : options.retries;
    const maxRetryTime = options.maxRetryTime ?? Number.POSITIVE_INFINITY;
    const context = Object.freeze({
        error: normalizedError,
        attemptNumber,
        retriesLeft,
        retriesConsumed,
    });
    await options.onFailedAttempt(context);
    if (calculateRemainingTime(startTime, maxRetryTime) <= 0) {
        throw normalizedError;
    }
    const consumeRetry = await options.shouldConsumeRetry(context);
    const remainingTime = calculateRemainingTime(startTime, maxRetryTime);
    if (remainingTime <= 0 || retriesLeft <= 0) {
        throw normalizedError;
    }
    if (normalizedError instanceof TypeError &&
        !(0, index_js_1.default)(normalizedError)) {
        if (consumeRetry) {
            throw normalizedError;
        }
        options.signal?.throwIfAborted();
        return false;
    }
    if (!(await options.shouldRetry(context))) {
        throw normalizedError;
    }
    if (!consumeRetry) {
        options.signal?.throwIfAborted();
        return false;
    }
    const delayTime = calculateDelay(retriesConsumed, options);
    const finalDelay = Math.min(delayTime, remainingTime);
    if (finalDelay > 0) {
        await new Promise((resolve, reject) => {
            const onAbort = () => {
                clearTimeout(timeoutToken);
                options.signal?.removeEventListener("abort", onAbort);
                reject(options.signal.reason);
            };
            const timeoutToken = setTimeout(() => {
                options.signal?.removeEventListener("abort", onAbort);
                resolve();
            }, finalDelay);
            if (options.unref) {
                timeoutToken.unref?.();
            }
            options.signal?.addEventListener("abort", onAbort, { once: true });
        });
    }
    options.signal?.throwIfAborted();
    return true;
}
async function pRetry(input, options = {}) {
    options = { ...options };
    validateRetries(options.retries);
    if (Object.hasOwn(options, "forever")) {
        throw new Error("The `forever` option is no longer supported. For many use-cases, you can set `retries: Infinity` instead.");
    }
    options.retries ??= 10;
    options.factor ??= 2;
    options.minTimeout ??= 1000;
    options.maxTimeout ??= Number.POSITIVE_INFINITY;
    options.maxRetryTime ??= Number.POSITIVE_INFINITY;
    options.randomize ??= false;
    options.onFailedAttempt ??= () => { };
    options.shouldRetry ??= () => true;
    options.shouldConsumeRetry ??= () => true;
    // Validate numeric options and normalize edge cases
    validateNumberOption("factor", options.factor, {
        min: 0,
        allowInfinity: false,
    });
    validateNumberOption("minTimeout", options.minTimeout, {
        min: 0,
        allowInfinity: false,
    });
    validateNumberOption("maxTimeout", options.maxTimeout, {
        min: 0,
        allowInfinity: true,
    });
    validateNumberOption("maxRetryTime", options.maxRetryTime, {
        min: 0,
        allowInfinity: true,
    });
    // Treat non-positive factor as 1 to avoid zero backoff or negative behavior
    if (!(options.factor > 0)) {
        options.factor = 1;
    }
    options.signal?.throwIfAborted();
    let attemptNumber = 0;
    let retriesConsumed = 0;
    const startTime = performance.now();
    while (Number.isFinite(options.retries) ? retriesConsumed <= options.retries : true) {
        attemptNumber++;
        try {
            options.signal?.throwIfAborted();
            const result = await input(attemptNumber);
            options.signal?.throwIfAborted();
            return result;
        }
        catch (error) {
            if (await onAttemptFailure({
                error,
                attemptNumber,
                retriesConsumed,
                startTime,
                options,
            })) {
                retriesConsumed++;
            }
        }
    }
    // Should not reach here, but in case it does, throw an error
    throw new Error("Retry attempts exhausted without throwing an error.");
}
function makeRetriable(function_, options) {
    return function (...arguments_) {
        return pRetry(() => function_.apply(this, arguments_), options);
    };
}
