/**
 * Blockscout — Chain 138 Besu RPC often returns empty tx lists in eth_getBlockByNumber.
 * Uses global /transactions pagination (fast) instead of per-block queries.
 */
export interface BlockscoutTx {
  hash: string;
  from: string;
  to: string;
  value: bigint;
  blockNumber: number;
  blockTimestamp: number;
}

interface BlockscoutItem {
  hash: string;
  value: string;
  block_number: number;
  timestamp: string;
  from: { hash: string };
  to: { hash: string } | null;
}

function parseItem(item: BlockscoutItem): BlockscoutTx | null {
  if (!item.hash || !item.from?.hash) return null;
  const ts = Math.floor(new Date(item.timestamp).getTime() / 1000);
  return {
    hash: item.hash,
    from: item.from.hash,
    to: item.to?.hash ?? '0x0000000000000000000000000000000000000000',
    value: BigInt(item.value || '0'),
    blockNumber: item.block_number,
    blockTimestamp: Number.isFinite(ts) ? ts : 0,
  };
}

/**
 * Fetch all validated txs with block_number in [fromBlock, toBlock] (inclusive).
 * Walks newest-first from Blockscout until block_number < fromBlock.
 */
export async function fetchTransactionsInBlockRange(
  apiBase: string,
  fromBlock: number,
  toBlock: number
): Promise<BlockscoutTx[]> {
  const base = apiBase.replace(/\/$/, '');
  const out: BlockscoutTx[] = [];
  const seen = new Set<string>();
  let url: string | null = `${base}/transactions?filter=validated`;
  let pages = 0;
  const maxPages = parseInt(process.env.CHECKPOINT_BLOCKSCOUT_MAX_PAGES || '5000', 10);

  const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

  while (url && pages < maxPages) {
    pages++;
    let res = await fetch(url);
    for (let attempt = 0; res.status === 429 && attempt < 5; attempt++) {
      await sleep(2000 * (attempt + 1));
      res = await fetch(url);
    }
    if (!res.ok) {
      throw new Error(`Blockscout ${res.status} ${url}`);
    }
    await sleep(parseInt(process.env.CHECKPOINT_BLOCKSCOUT_PAGE_DELAY_MS || '80', 10));
    const body = (await res.json()) as {
      items: BlockscoutItem[];
      next_page_params?: Record<string, string | number | null>;
    };
    let stop = false;
    for (const item of body.items ?? []) {
      const bn = item.block_number;
      if (bn < fromBlock) {
        stop = true;
        break;
      }
      if (bn > toBlock) continue;
      const tx = parseItem(item);
      if (!tx || seen.has(tx.hash.toLowerCase())) continue;
      seen.add(tx.hash.toLowerCase());
      out.push(tx);
    }
    if (stop) break;
    const next = body.next_page_params;
    if (!next) break;
    const q = new URLSearchParams();
    for (const [k, v] of Object.entries(next)) {
      if (v !== null && v !== undefined) q.set(k, String(v));
    }
    url = `${base}/transactions?${q.toString()}`;
  }

  out.sort((a, b) => a.blockNumber - b.blockNumber || a.blockTimestamp - b.blockTimestamp);
  return out;
}
