/**
 * Payment-Gated API Example (TypeScript / Next.js)
 *
 * Demonstrates how to build an x402-gated API endpoint in Next.js.
 * The server returns a 402 with payment requirements when no payment
 * is present, and the resource when payment is valid.
 *
 * This example includes:
 *   - Server: how to emit a 402 with payment requirements
 *   - Client: how to use x402Fetch to automatically pay and retry
 *   - Server: how to verify the incoming X-PAYMENT header
 *
 * Relevant x402 spec:
 *   https://github.com/coinbase/x402
 *   https://x402.org/facilitator
 */

import {
  configureX402,
  x402Fetch,
  isPaymentRequired,
  parsePaymentRequired,
  selectPaymentRequirement,
  payAndRetry,
  verifyPaymentSignature,
  encodeHeaderPayload,
  decodeHeaderPayload,
  BASE_USDC_ADDRESS,
  BASE_NETWORK,
  HEADERS,
  X402_VERSION,
  X402PaymentRequired,
  X402PaymentPayload,
  WalletProvider,
} from '../src/x402-client';

// ---------------------------------------------------------------------------
// SERVER SIDE: How to build a payment-gated Next.js route
// ---------------------------------------------------------------------------

/**
 * Example Next.js API route that requires x402 payment.
 *
 * Place this file at: app/api/premium-data/route.ts
 *
 * The route:
 *   1. Checks for X-Payment header
 *   2. If absent, returns 402 with payment requirements
 *   3. If present, verifies the payment and returns the data
 */

// USDC price per call: 0.001 USDC (= 1000 micro-USDC)
const PRICE_MICRO_USDC = '1000'; // 1000 = $0.001 USDC
const RECIPIENT_ADDRESS = '0xfA15Ee28939B222B0448261A22156070f0A7813C'; // Vaultfire deployer

export async function serverRouteHandler(request: Request): Promise<Response> {
  const paymentHeader = request.headers.get(HEADERS.PAYMENT_SIGNATURE);

  // ----- No payment header: return 402 -----
  if (!paymentHeader) {
    const requirements: X402PaymentRequired = {
      x402Version: X402_VERSION,
      accepts: [
        {
          scheme: 'exact',
          network: BASE_NETWORK,
          amount: PRICE_MICRO_USDC,
          asset: BASE_USDC_ADDRESS,
          payTo: RECIPIENT_ADDRESS,
          maxTimeoutSeconds: 60,
          extra: {
            assetTransferMethod: 'eip3009',
            name: 'USDC',
            version: '2',
          },
        },
      ],
      resource: {
        url: 'https://api.example.com/premium-data',
        description: 'Premium market data — real-time USDC feed',
        mimeType: 'application/json',
      },
    };

    return new Response(JSON.stringify(requirements), {
      status: 402,
      headers: {
        'Content-Type': 'application/json',
        [HEADERS.PAYMENT_REQUIRED]: encodeHeaderPayload(requirements),
      },
    });
  }

  // ----- Payment header present: verify -----
  let payload: X402PaymentPayload;
  try {
    payload = decodeHeaderPayload<X402PaymentPayload>(paymentHeader);
  } catch {
    return new Response(JSON.stringify({ error: 'Invalid payment header encoding' }), {
      status: 400,
      headers: { 'Content-Type': 'application/json' },
    });
  }

  // Verify the EIP-712 signature locally
  const { valid, recoveredAddress, error } = await verifyPaymentSignature(payload);
  if (!valid) {
    return new Response(JSON.stringify({ error: `Invalid payment signature: ${error}` }), {
      status: 402,
      headers: { 'Content-Type': 'application/json' },
    });
  }

  // Validate amount
  if (BigInt(payload.accepted.amount) < BigInt(PRICE_MICRO_USDC)) {
    return new Response(
      JSON.stringify({
        error: `Insufficient payment: received ${payload.accepted.amount} micro-USDC, required ${PRICE_MICRO_USDC}`,
      }),
      { status: 402, headers: { 'Content-Type': 'application/json' } },
    );
  }

  // Payment verified — submit to facilitator for settlement (async)
  // In production: call https://x402.org/facilitator/settle or your own relayer
  // For demo, we proceed immediately after signature verification
  console.log(
    `[x402] Payment verified: ${payload.accepted.amount} micro-USDC from ${recoveredAddress}`,
  );

  // Return the protected resource
  const data = {
    timestamp: new Date().toISOString(),
    btcUsd: 67_420.50,
    ethUsd: 3_520.75,
    baseUsdc: BASE_USDC_ADDRESS,
    payer: recoveredAddress,
    amountPaid: payload.accepted.amount,
  };

  return new Response(JSON.stringify(data), {
    status: 200,
    headers: {
      'Content-Type': 'application/json',
      [HEADERS.PAYMENT_RESPONSE]: encodeHeaderPayload({
        success: true,
        network: BASE_NETWORK,
      }),
    },
  });
}

// ---------------------------------------------------------------------------
// CLIENT SIDE: How to call a payment-gated API
// ---------------------------------------------------------------------------

async function callPaymentGatedApi() {
  // Configure wallet (in a real app, use your wallet integration)
  const mockWallet: WalletProvider = {
    getAddress: () => '0x1234567890123456789012345678901234567890',
    getPrivateKey: () => process.env['VAULTFIRE_AGENT_KEY'] ?? null,
    isUnlocked: () => !!process.env['VAULTFIRE_AGENT_KEY'],
  };
  configureX402(mockWallet);

  const PAID_API = 'https://api.example.com/premium-data';

  console.log('\n=== CLIENT: Calling payment-gated API ===');

  // Option A: Auto-pay (x402Fetch handles everything)
  const result = await x402Fetch(
    PAID_API,
    { method: 'GET' },
    {
      autoPay: true,
      maxAutoPayAmount: '0.01',
      facilitatorUrl: 'https://x402.org/facilitator',
    },
  );

  if (result.paid) {
    console.log('[x402] Payment successful!');
    const data = await result.response.json();
    console.log('[x402] Data received:', data);
  } else {
    console.log('[x402] Not paid:', result.error);
  }

  // Option B: Manual flow (inspect requirements before paying)
  const initialResponse = await fetch(PAID_API);
  if (isPaymentRequired(initialResponse)) {
    const requirements = await parsePaymentRequired(initialResponse);
    const selected = requirements ? selectPaymentRequirement(requirements) : null;

    if (selected) {
      console.log(`\n[x402] Payment required: ${selected.amount} micro-USDC to ${selected.payTo}`);
      console.log(`[x402] Asset: ${selected.asset}`);
      console.log(`[x402] Network: ${selected.network}`);

      // User can confirm here before paying
      const confirmed = true; // prompt user in a real UI
      if (confirmed) {
        const paidResult = await payAndRetry(PAID_API, {}, selected, requirements?.resource);
        console.log('[x402] Paid and got response:', paidResult.response.status);
      }
    }
  }
}

// ---------------------------------------------------------------------------
// Run client demo
// ---------------------------------------------------------------------------

if (require.main === module) {
  callPaymentGatedApi().catch(console.error);
}
