/**
 * GET /api/x402/oracle/chainlink-status — FREE (no payment required)
 *
 * Discovery + freshness beacon for the Vaultfire Chainlink oracle namespace.
 * Returns the full feed support matrix across Base, Avalanche, Arbitrum, and
 * Polygon, with live on-chain freshness for each (chain, pair) combination.
 *
 * Designed for x402 crawlers, agent-directory bots, and humans previewing
 * the namespace before paying for /price-feed or /agent-stake-usd.
 *
 * Reads all 15 feeds in parallel via Promise.allSettled, so a single bad
 * RPC never breaks the response — partial results are returned with the
 * failed entry's `fresh` set to false and an `error` field.
 *
 * Vaultfire does NOT operate these feeds. Source of truth is Chainlink's
 * decentralized oracle network — this endpoint just reads the on-chain
 * AggregatorV3 proxies and surfaces them for agents.
 */

import { NextRequest, NextResponse } from 'next/server';
import { ethers } from 'ethers';
import { withFailover } from '../../../_lib/rpc-retry';
import {
  FEEDS,
  AGGREGATOR_V3_ABI,
  SUPPORTED_CHAINS,
  SUPPORTED_PAIRS,
  allFeeds,
  type ChainKey,
  type PairKey,
  type FeedEntry,
} from '../_lib/feeds';

// 24h threshold — Chainlink heartbeats are always well under this.
const FRESH_THRESHOLD_SECONDS = 86400;

interface FeedReading {
  pair: PairKey;
  chain: ChainKey;
  feedAddress: string;
  explorerUrl: string;
  decimals: number;
  answer: number | null;
  answerRaw: string | null;
  roundId: string | null;
  updatedAt: number | null;
  ageSeconds: number | null;
  fresh: boolean;
  note?: string;
  error?: string;
}

async function readOne(
  chain: ChainKey,
  pair: PairKey,
  feed: FeedEntry,
): Promise<FeedReading> {
  const base: FeedReading = {
    pair,
    chain,
    feedAddress: feed.address,
    explorerUrl: feed.explorerUrl,
    decimals: feed.decimals,
    answer: null,
    answerRaw: null,
    roundId: null,
    updatedAt: null,
    ageSeconds: null,
    fresh: false,
    ...(feed.note ? { note: feed.note } : {}),
  };

  try {
    const roundData = await withFailover(chain, async (provider) => {
      const c = new ethers.Contract(feed.address, AGGREGATOR_V3_ABI, provider);
      return (await c.latestRoundData()) as [bigint, bigint, bigint, bigint, bigint];
    });
    const [roundId, answerRaw, , updatedAt] = roundData;
    const nowSec = Math.floor(Date.now() / 1000);
    const ageSeconds = nowSec - Number(updatedAt);
    return {
      ...base,
      answer: Number(answerRaw) / 10 ** feed.decimals,
      answerRaw: answerRaw.toString(),
      roundId: roundId.toString(),
      updatedAt: Number(updatedAt),
      ageSeconds,
      fresh: ageSeconds >= 0 && ageSeconds < FRESH_THRESHOLD_SECONDS,
    };
  } catch (e) {
    const err = e as { shortMessage?: string; message?: string };
    return {
      ...base,
      error: err?.shortMessage || err?.message || String(e),
    };
  }
}

export async function GET(_req: NextRequest) {
  const all = allFeeds();
  const results = await Promise.allSettled(
    all.map(({ chain, pair, entry }) => readOne(chain, pair, entry)),
  );

  // Bucket per-chain
  const byChain: Record<ChainKey, Partial<Record<PairKey, FeedReading>>> = {
    base: {},
    avalanche: {},
    arbitrum: {},
    polygon: {},
  };
  let freshCount = 0;
  let staleCount = 0;
  let errorCount = 0;

  for (let i = 0; i < results.length; i++) {
    const r = results[i];
    const meta = all[i];
    if (r.status === 'fulfilled') {
      byChain[meta.chain][meta.pair] = r.value;
      if (r.value.error) errorCount++;
      else if (r.value.fresh) freshCount++;
      else staleCount++;
    } else {
      byChain[meta.chain][meta.pair] = {
        pair: meta.pair,
        chain: meta.chain,
        feedAddress: meta.entry.address,
        explorerUrl: meta.entry.explorerUrl,
        decimals: meta.entry.decimals,
        answer: null,
        answerRaw: null,
        roundId: null,
        updatedAt: null,
        ageSeconds: null,
        fresh: false,
        error: String(r.reason),
        ...(meta.entry.note ? { note: meta.entry.note } : {}),
      };
      errorCount++;
    }
  }

  return NextResponse.json(
    {
      service: 'vaultfire-oracle',
      name: 'Vaultfire x Chainlink Oracle Namespace',
      description:
        'Free discovery beacon for the Vaultfire Chainlink oracle endpoints. Lists every supported (chain, pair) feed with live freshness. Vaultfire reads Chainlink AggregatorV3 proxies on-chain and exposes them as x402 paid endpoints — Chainlink is the source of truth for market data, Vaultfire is the source of truth for agent trust.',
      status: errorCount === 0 ? 'ok' : (freshCount > 0 ? 'degraded' : 'down'),
      version: 1,
      source: 'chainlink',
      attribution:
        'Price data provided by Chainlink (https://chain.link). Vaultfire does not operate these feeds.',
      supportedChains: SUPPORTED_CHAINS,
      supportedPairs: SUPPORTED_PAIRS,
      totals: {
        feeds: all.length,
        fresh: freshCount,
        stale: staleCount,
        errors: errorCount,
      },
      pricedEndpoints: [
        {
          path: '/api/x402/oracle/price-feed',
          method: 'GET',
          price: '$0.01 USDC',
          description:
            'Read any supported Chainlink Data Feed (pair × chain). Returns answer, decimals, roundId, updatedAt, freshness.',
          params: ['pair (required)', 'chain (default: base)'],
        },
        {
          path: '/api/x402/oracle/agent-stake-usd',
          method: 'GET',
          price: '$0.02 USDC',
          description:
            'USD-denominated agent stake — composes Vaultfire bond (ETH) × Chainlink ETH/USD price. Uniquely enabled by the Chainlink integration.',
          params: ['address (required)', 'chain (default: base)'],
        },
      ],
      paymentNetworks: ['eip155:8453', 'solana'],
      paywallAccepts: ['USDC'],
      threshold: { freshSeconds: FRESH_THRESHOLD_SECONDS },
      feeds: byChain,
      checkedAt: new Date().toISOString(),
    },
    {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Cache-Control': 'public, max-age=30, s-maxage=30, stale-while-revalidate=15',
      },
    },
  );
}
