/**
 * POST /api/x402/actions/file-attestation
 *
 * x402 payment-gated tx-prepare for belief attestation — $0.25 USDC.
 *
 * Encodes a verifyProof(bytes,uint256[]) call against BeliefAttestationVerifier
 * on the requested chain. Returns an unsigned transaction the caller signs in
 * their own wallet. NO private keys are ever accepted server-side.
 *
 * Body (JSON):
 *   - walletAddress: string (the agent filing the attestation)
 *   - proofBytes: string (hex-encoded zk proof, "0x..." prefix optional)
 *   - publicInputs: string[] | number[] (uint256 values as strings or numbers)
 *   - chain?: "base" | "avalanche" | "arbitrum" | "polygon" (default: "base")
 *
 * Returns:
 *   txData: { to, data, value, chainId, gasEstimate } — sign in your wallet.
 */

import { NextRequest, NextResponse } from 'next/server';
import { withX402 } from 'x402-next';
import { facilitator } from '@coinbase/x402';
import { withActionLogging } from '../_lib/log-action';
import {
  CHAIN_IDS, RPC_URLS, EXPLORERS, SUPPORTED_CHAINS,
  isValidAddress, isValidChain, encodeUint256,
  estimateGas, rejectsPrivateKeyShape,
} from '../_lib/tx-encode';

const PAY_TO = '0xfA15Ee28939B222B0448261A22156070f0A7813C';

const BELIEF_ATTESTATION: Record<string, string> = {
  base:      '0x32211773C8266774A704aeD471f8eF073f34bA79',
  avalanche: '0x2711AA5f1934Fe97825F02e51cB5Da58d1759625',
  arbitrum:  '0xaaC44F1396f3F5b3C06048a1F819DC9b0f86D90c',
  polygon:   '0xBc4208A7A59af3B7E438d3308C3dc0d299d739a7',
};

function normalizeHex(input: string): string {
  let h = input.trim();
  if (h.startsWith('0x') || h.startsWith('0X')) h = h.slice(2);
  if (!/^[0-9a-fA-F]*$/.test(h)) throw new Error('proofBytes must be hex');
  if (h.length % 2 !== 0) h = '0' + h;
  return h.toLowerCase();
}

function encodeBytesPayload(hexBody: string): string {
  // ABI-encode `bytes`: length (uint256) + padded data
  const byteLen = hexBody.length / 2;
  const length = encodeUint256(byteLen);
  const paddedLength = Math.ceil(byteLen / 32) * 32;
  const padded = hexBody.padEnd(paddedLength * 2, '0');
  return length + padded;
}

function encodeUint256Array(values: (string | number)[]): { head: string; body: string } {
  // dynamic uint256[] = length (head) + each value (body)
  const length = encodeUint256(values.length);
  let body = '';
  for (const v of values) {
    body += encodeUint256(BigInt(v));
  }
  return { head: length, body };
}

const handler = async (req: NextRequest) => {
  let body: any;
  try { body = await req.json(); } catch { body = {}; }

  const keyErr = rejectsPrivateKeyShape(body);
  if (keyErr) return NextResponse.json({ error: keyErr }, { status: 400 });

  const { walletAddress, proofBytes, publicInputs, chain = 'base' } = body;

  if (!walletAddress || !proofBytes || !publicInputs) {
    return NextResponse.json({ error: 'Missing required fields: walletAddress, proofBytes, publicInputs' }, { status: 400 });
  }
  if (!isValidAddress(walletAddress)) {
    return NextResponse.json({ error: 'Invalid walletAddress' }, { status: 400 });
  }
  if (!isValidChain(chain)) {
    return NextResponse.json({ error: `Invalid chain. Use: ${SUPPORTED_CHAINS.join(', ')}` }, { status: 400 });
  }
  if (!Array.isArray(publicInputs) || publicInputs.length === 0) {
    return NextResponse.json({ error: 'publicInputs must be a non-empty array of uint256 values' }, { status: 400 });
  }

  let proofHex: string;
  try { proofHex = normalizeHex(String(proofBytes)); }
  catch { return NextResponse.json({ error: 'proofBytes must be hex' }, { status: 400 }); }

  // Validate inputs
  let inputsBig: bigint[];
  try { inputsBig = publicInputs.map((v: any) => BigInt(v)); }
  catch { return NextResponse.json({ error: 'publicInputs must be uint256-compatible' }, { status: 400 }); }
  for (const v of inputsBig) {
    if (v < 0n || v >= (1n << 256n)) {
      return NextResponse.json({ error: 'publicInput out of uint256 range' }, { status: 400 });
    }
  }

  // Encode verifyProof(bytes, uint256[]) — selector 0x1e8e1e13
  // Layout: [head1=offset to bytes][head2=offset to uint256[]][bytes data][uint256[] data]
  const proofPart = encodeBytesPayload(proofHex);              // bytes (len + padded data)
  const arrPart = encodeUint256Array(inputsBig.map(v => v.toString()));

  const head1Offset = 0x40;                                    // 2 * 32
  const head2Offset = head1Offset + proofPart.length / 2;
  const calldata = '0x1e8e1e13'
    + encodeUint256(head1Offset)
    + encodeUint256(head2Offset)
    + proofPart
    + arrPart.head + arrPart.body;

  const verifier = BELIEF_ATTESTATION[chain];
  const rpc = RPC_URLS[chain];
  const chainId = CHAIN_IDS[chain];
  const gasEstimate = await estimateGas(rpc, walletAddress, verifier, calldata);

  return NextResponse.json({
    success: true,
    txData: {
      to: verifier,
      data: calldata,
      value: '0',
      chainId,
      gasEstimate,
    },
    chain,
    contract: verifier,
    explorer: `${EXPLORERS[chain]}/address/${verifier}`,
    message: `Sign in your wallet (${walletAddress}) to file a belief attestation on ${chain}. The verifier will check your zk proof against the public inputs.`,
    securityNote: 'Vaultfire NEVER accepts private keys. Sign locally in your Base App / MetaMask / WalletConnect.',
    _x402: {
      paid: true,
      price: '$0.25 USDC',
      service: 'vaultfire-trust',
      facilitator: 'cdp',
      pattern: 'tx-prepare (caller signs)',
    },
  }, { headers: { 'Access-Control-Allow-Origin': '*', 'Cache-Control': 'no-store' } });
};

export const POST = withX402(
  withActionLogging(handler, '$0.25', '/actions/file-attestation'),
  PAY_TO,
  {
    price: '$0.25',
    network: 'base',
    config: {
      description: 'File a belief attestation on the BeliefAttestationVerifier contract (zk proof + public inputs). Pay $0.25 USDC, receive a fully-encoded unsigned transaction, sign in your wallet. No private keys handled server-side.',
      mimeType: 'application/json',
      outputSchema: {
        input: {
          type: 'http',
          method: 'POST',
          discoverable: true,
          bodySchema: {
            type: 'object',
            required: ['walletAddress', 'proofBytes', 'publicInputs'],
            properties: {
              walletAddress: { type: 'string', description: 'Wallet that will sign the attestation' },
              proofBytes: { type: 'string', description: 'Hex-encoded zk proof (0x prefix optional)' },
              publicInputs: { type: 'array', items: { type: 'string' }, description: 'uint256 values' },
              chain: { type: 'string', enum: ['base', 'avalanche', 'arbitrum', 'polygon'] },
            },
          },
        },
        output: {
          type: 'object',
          properties: {
            success: { type: 'boolean' },
            txData: { type: 'object' },
            chain: { type: 'string' },
            explorer: { type: 'string' },
            message: { type: 'string' },
          },
        },
      },
    },
  },
  facilitator,
);

export async function OPTIONS() {
  return new NextResponse(null, {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Methods': 'POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type',
    },
  });
}
