import axios from "axios";
const RETRYABLE_HTTP_STATUSES = new Set([429, 502, 503]);
function isRetryable(error) {
    if (!axios.isAxiosError(error))
        return false;
    if (error.response?.status !== undefined) {
        return RETRYABLE_HTTP_STATUSES.has(error.response.status);
    }
    return error.code === "ETIMEDOUT";
}
export async function withRetry(fn, options = {}) {
    const { maxRetries = 3, baseDelayMs = 1000, maxDelayMs = 30_000 } = options;
    for (let attempt = 0; attempt <= maxRetries; attempt++) {
        try {
            return await fn();
        }
        catch (error) {
            if (attempt === maxRetries)
                throw error;
            if (!isRetryable(error))
                throw error;
            const status = axios.isAxiosError(error) ? error.response?.status : undefined;
            let delayMs;
            if (status === 429) {
                const retryAfter = axios.isAxiosError(error) ? error.response?.headers["retry-after"] : undefined;
                if (retryAfter) {
                    const parsed = parseInt(retryAfter, 10);
                    if (Number.isFinite(parsed) && parsed > 0) {
                        delayMs = Math.min(parsed * 1000, maxDelayMs);
                    }
                    else {
                        // Try HTTP-date format
                        const dateMs = Date.parse(retryAfter);
                        if (Number.isFinite(dateMs)) {
                            delayMs = Math.max(dateMs - Date.now(), 1000);
                        }
                        else {
                            // Unparseable, fall back to exponential
                            delayMs = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
                            delayMs += Math.random() * delayMs * 0.1;
                        }
                    }
                }
                else {
                    delayMs = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
                    delayMs += Math.random() * delayMs * 0.1;
                }
            }
            else {
                // 502, 503, ETIMEDOUT — always exponential backoff
                delayMs = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
                delayMs += Math.random() * delayMs * 0.1;
            }
            await new Promise((r) => setTimeout(r, delayMs));
        }
    }
    throw new Error("Unreachable");
}
//# sourceMappingURL=retry.js.map