/**
 * Retry utility for RPC calls with fallback endpoints.
 *
 * Public RPCs rate-limit aggressively. These helpers retry failed
 * contract reads, rotating across fallback endpoints on each attempt.
 */

import { ethers } from 'ethers';

/** Primary + fallback RPC endpoints per chain — all free/public. */
export const RPC_FALLBACKS: Record<string, string[]> = {
  base:      ['https://base-rpc.publicnode.com', 'https://mainnet.base.org'],
  avalanche: ['https://api.avax.network/ext/bc/C/rpc', 'https://avalanche-c-chain-rpc.publicnode.com'],
  arbitrum:  ['https://arb1.arbitrum.io/rpc', 'https://arbitrum-one-rpc.publicnode.com'],
  polygon:   ['https://polygon-bor-rpc.publicnode.com', 'https://polygon.llamarpc.com', 'https://polygon.drpc.org'],
};

/**
 * Retry an async operation up to `retries` times with exponential backoff,
 * against the SAME RPC. Prefer `withFailover` when you want to rotate RPCs.
 */
export async function withRetry<T>(
  fn: () => Promise<T>,
  { retries = 2, baseDelayMs = 400 } = {},
): Promise<T> {
  let lastError: unknown;
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (err) {
      lastError = err;
      if (attempt < retries) {
        await new Promise((r) => setTimeout(r, baseDelayMs * 2 ** attempt));
      }
    }
  }
  throw lastError;
}

/**
 * Run `fn` against each RPC endpoint in `RPC_FALLBACKS[chain]` until one
 * succeeds. A fresh `ethers.JsonRpcProvider` is created for every attempt
 * and passed to the callback. Throws the last error if all RPCs fail.
 *
 * Use this instead of constructing a provider at module scope — it
 * recovers automatically when an RPC rate-limits or times out.
 */
export async function withFailover<T>(
  chain: string,
  fn: (provider: ethers.JsonRpcProvider) => Promise<T>,
  { perRpcRetries = 1, baseDelayMs = 250 } = {},
): Promise<T> {
  const rpcs = RPC_FALLBACKS[chain];
  if (!rpcs || rpcs.length === 0) {
    throw new Error(`No RPC endpoints configured for chain: ${chain}`);
  }

  let lastError: unknown;
  for (const url of rpcs) {
    for (let attempt = 0; attempt <= perRpcRetries; attempt++) {
      try {
        const provider = new ethers.JsonRpcProvider(url);
        return await fn(provider);
      } catch (err) {
        lastError = err;
        if (attempt < perRpcRetries) {
          await new Promise((r) => setTimeout(r, baseDelayMs * 2 ** attempt));
        }
      }
    }
  }
  throw lastError;
}
