/**
 * Comma-separated RPC URL pool with round-robin and read-call failover.
 */

export function parseRpcPool(...sources) {
  const seen = new Set();
  const urls = [];
  for (const source of sources) {
    if (!source || String(source).includes('${')) continue;
    for (const part of String(source).split(',')) {
      const url = part.trim();
      if (!url || seen.has(url)) continue;
      seen.add(url);
      urls.push(url);
    }
  }
  return urls;
}

export class RpcUrlPool {
  constructor(urls, logger) {
    this.urls = Array.isArray(urls) ? urls.filter(Boolean) : [];
    this.logger = logger;
    this._cursor = 0;
  }

  get size() {
    return this.urls.length;
  }

  primaryUrl() {
    if (this.urls.length === 0) return '';
    return this.urls[0];
  }

  nextUrl() {
    if (this.urls.length === 0) return '';
    const url = this.urls[this._cursor % this.urls.length];
    this._cursor += 1;
    return url;
  }

  /** Run async read fn(url) until one succeeds or all fail. */
  async withFailover(label, fn) {
    if (this.urls.length === 0) {
      throw new Error(`${label}: RPC pool is empty`);
    }
    let lastError;
    for (const url of this.urls) {
      try {
        return await fn(url);
      } catch (error) {
        lastError = error;
        this.logger?.warn?.(`${label} RPC failed (${url}): ${error?.message || error}`);
      }
    }
    throw lastError || new Error(`${label}: all RPC pool URLs failed`);
  }
}
