import { NextResponse } from 'next/server';

/**
 * GET /api/protocol/distributions
 *
 * Returns the live count of dispute filings and bond distributions across all
 * chains, read directly from the canonical contracts.
 *
 * Source of truth:
 *   - VaultfireDisputeResolution.getDisputeStats() →
 *       (filed, resolved, totalStake)
 *   - AccountabilityBondV2.totalPendingDistributions() →
 *       sum of pending distribution amounts (raw 18-decimal native units)
 *   - AccountabilityBondV2.totalActiveBondValue() →
 *       sum of currently locked bond value (raw 18-decimal native units)
 *
 * This endpoint exists because there is NO `Slashed` event in the contract.
 * The accountability mechanism is flourishing-weighted distribution via
 * `BondDistributed` events, with disputes resolved by an arbitrator panel
 * in `VaultfireDisputeResolution`. Surfacing real numbers honestly:
 * if zero disputes have ever been filed, this endpoint says zero.
 */

export const dynamic = 'force-dynamic';
export const revalidate = 0;

const CORS_HEADERS = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type',
};

export async function OPTIONS() {
  return new NextResponse(null, { status: 204, headers: CORS_HEADERS });
}

const CHAINS = {
  base: {
    rpcs: ['https://mainnet.base.org', 'https://base-rpc.publicnode.com'],
    accountability: '0x6750D28865434344e04e1D0a6044394b726C3dfE',
    disputes:       '0x3a681b6eF98c6d35643Ca631eE50EF35edd6BcBD',
  },
  avalanche: {
    rpcs: ['https://api.avax.network/ext/bc/C/rpc', 'https://avalanche-c-chain-rpc.publicnode.com'],
    accountability: '0x376831fB2457E34559891c32bEb61c442053C066',
    disputes:       '0x98afd1440B2238D73c1394720277a6d031fCbbD0',
  },
  arbitrum: {
    rpcs: ['https://arb1.arbitrum.io/rpc', 'https://arbitrum-one-rpc.publicnode.com'],
    accountability: '0xef3A944f4d7bb376699C83A29d7Cb42C90D9B6F0',
    disputes:       '0x152a0446958f48EB3F1f5126FAF418D663359e95',
  },
  polygon: {
    rpcs: ['https://polygon-bor-rpc.publicnode.com'],
    accountability: '0xdB54B8925664816187646174bdBb6Ac658A55a5F',
    disputes:       '0x5F7680a9a720c03812532A735D1165D9f97c0Cd1',
  },
} as const;

// Function selectors (first 4 bytes of keccak256("name()"))
const SEL_DISPUTE_STATS              = '0x1924b4ed'; // getDisputeStats() → (filed, resolved, totalStake)
const SEL_TOTAL_ACTIVE_BOND_VALUE    = '0xe7683e58'; // totalActiveBondValue()
const SEL_TOTAL_PENDING_DISTRIBUTIONS = '0x8ce8136a'; // totalPendingDistributions()

async function rpcCallOne(url: string, to: string, data: string): Promise<string | null> {
  try {
    const res = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call', params: [{ to, data }, 'latest'] }),
      signal: AbortSignal.timeout(8000),
    });
    const json = await res.json();
    if (json.error || !json.result || json.result === '0x') return null;
    return json.result as string;
  } catch {
    return null;
  }
}

async function rpcCall(rpcs: readonly string[], to: string, data: string): Promise<string | null> {
  for (const url of rpcs) {
    const r = await rpcCallOne(url, to, data);
    if (r) return r;
  }
  return null;
}

/** Decode `getDisputeStats()` return: (uint256 filed, uint256 resolved, uint256 totalStake). */
function decodeDisputeStats(raw: string | null): { filed: number; resolved: number; totalStake: string } {
  if (!raw) return { filed: 0, resolved: 0, totalStake: '0' };
  const hex = raw.replace('0x', '');
  if (hex.length < 192) return { filed: 0, resolved: 0, totalStake: '0' };
  const filed = parseInt(hex.slice(0, 64), 16);
  const resolved = parseInt(hex.slice(64, 128), 16);
  const totalStakeRaw = BigInt('0x' + hex.slice(128, 192)).toString();
  return { filed, resolved, totalStake: totalStakeRaw };
}

/** Decode a single uint256 return value to a decimal string (preserves precision). */
function decodeUint256(raw: string | null): string {
  if (!raw) return '0';
  try {
    return BigInt(raw).toString();
  } catch {
    return '0';
  }
}

interface ChainResult {
  chain: string;
  disputesFiled: number;
  disputesResolved: number;
  totalDisputeStake: string;       // raw native units (string for precision)
  totalActiveBondValue: string;    // raw native units
  totalPendingDistributions: string; // raw native units
}

async function fetchChain(chain: string, cfg: typeof CHAINS[keyof typeof CHAINS]): Promise<ChainResult> {
  const [stats, activeBonds, pending] = await Promise.all([
    rpcCall(cfg.rpcs, cfg.disputes,       SEL_DISPUTE_STATS),
    rpcCall(cfg.rpcs, cfg.accountability, SEL_TOTAL_ACTIVE_BOND_VALUE),
    rpcCall(cfg.rpcs, cfg.accountability, SEL_TOTAL_PENDING_DISTRIBUTIONS),
  ]);

  const decoded = decodeDisputeStats(stats);

  return {
    chain,
    disputesFiled:             decoded.filed,
    disputesResolved:          decoded.resolved,
    totalDisputeStake:         decoded.totalStake,
    totalActiveBondValue:      decodeUint256(activeBonds),
    totalPendingDistributions: decodeUint256(pending),
  };
}

export async function GET() {
  const tasks: Promise<ChainResult>[] = [];
  for (const [chain, cfg] of Object.entries(CHAINS)) {
    tasks.push(fetchChain(chain, cfg));
  }
  const results = await Promise.all(tasks);

  const totalDisputesFiled    = results.reduce((s, r) => s + r.disputesFiled, 0);
  const totalDisputesResolved = results.reduce((s, r) => s + r.disputesResolved, 0);

  // Build per-chain map without leaking duplicates.
  const perChain: Record<string, Omit<ChainResult, 'chain'>> = {};
  for (const r of results) {
    const { chain: _chain, ...rest } = r;
    perChain[r.chain] = rest;
  }

  return NextResponse.json(
    {
      // Headline numbers — what the page should display.
      totalDisputesFiled,
      totalDisputesResolved,
      // Whether any distribution events have ever happened in production.
      // We expose dispute count as the canonical "has the resolution
      // mechanism been used" signal, since `BondDistributed` requires a
      // dispute to have driven the metrics challenge in the typical flow.
      mechanismUsed: totalDisputesFiled > 0,
      // Per-chain raw data for anyone who wants to verify on-chain.
      perChain,
      // Honest note about the mechanism — kept here so any consumer of
      // this API surface gets the same framing as the UI.
      note:
        'AccountabilityBondV2 distributes bonds via flourishing-weighted ' +
        'shares (BondDistributed event), not slashing. Disputes are ' +
        'resolved by an arbitrator panel in VaultfireDisputeResolution.',
      contracts: {
        accountability: Object.fromEntries(
          Object.entries(CHAINS).map(([c, cfg]) => [c, cfg.accountability]),
        ),
        disputes: Object.fromEntries(
          Object.entries(CHAINS).map(([c, cfg]) => [c, cfg.disputes]),
        ),
      },
      timestamp: Date.now(),
    },
    { headers: CORS_HEADERS },
  );
}
