/**
 * Multi-network x402 wrapper for Vaultfire trust GET endpoints.
 *
 * Drop-in replacement for `withX402(handler, payTo, routeConfig, facilitator)`
 * from x402-next that accepts USDC on BOTH:
 *   - Base mainnet (EIP-3009 transferWithAuthorization, scheme: exact, network: eip155:8453)
 *   - Solana mainnet (SPL transfer signature verification, scheme: exact, network: solana)
 *
 * Why a custom wrapper instead of x402-next's withX402?
 *   The x402-next SDK's RouteConfig.network is a single Network enum value,
 *   so a single route can only advertise one network. Other Bazaar resources
 *   (e.g. api.oatp.cc) accept both networks on the same endpoint by using
 *   their own paywall middleware — this matches that pattern.
 *
 * The 402 response advertises BOTH networks in `accepts`. Clients pick whichever
 * USDC they hold and post the proof in `Payment-Signature` (or `X-Payment` for v1
 * compatibility). The server verifies the proof against the correct chain and
 * grants access.
 *
 * Trust GETs only — POST action endpoints continue to use `withX402` until
 * x402-next supports multi-network route configs.
 */

import { NextRequest, NextResponse } from 'next/server';
import { verifyX402Payment } from './verify-payment';
import { recordPaidCall, priceToMicro } from '../../../_lib/payer-log';

interface RouteConfig {
  /** Price in dollar-string format, e.g. '$0.01' */
  price: string;
  /** Description for the 402 paywall page */
  description: string;
  /** MIME type of successful response */
  mimeType?: string;
  /** Output schema (informational, included in 402 response) */
  outputSchema?: Record<string, unknown>;
  /** Input schema describing accepted query/body parameters (JSON Schema). */
  inputSchema?: Record<string, unknown>;
  /** Example output payload (becomes bazaar.info.output.example). */
  outputExample?: unknown;
  /** Example input payload (becomes bazaar.info.input.queryParams or .body). */
  inputExample?: Record<string, unknown>;
  /** HTTP method (defaults to GET). */
  method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
  /** Whether endpoint should be discoverable in the Bazaar */
  discoverable?: boolean;
}

type Handler = (req: NextRequest) => Promise<Response> | Response;

/**
 * Convert a price like '$0.01' → atomic USDC ('10000', 6 decimals).
 */
function priceToAtomic(price: string): string {
  const cleaned = price.replace(/[$\s]/g, '').replace(/USDC/i, '');
  const usd = parseFloat(cleaned);
  if (!Number.isFinite(usd) || usd < 0) {
    throw new Error(`Invalid price: ${price}`);
  }
  return Math.round(usd * 1_000_000).toString();
}

/**
 * Safely extract the request path. Falls back to 'unknown' if URL parsing fails.
 */
function safePath(req: NextRequest): string {
  try {
    return req.nextUrl?.pathname || 'unknown';
  } catch {
    return 'unknown';
  }
}

/**
 * Wrap a Next.js route handler with x402 paywall accepting Base + Solana USDC.
 *
 * Logs three signals to Vercel runtime logs (greppable prefixes):
 *   [x402-402]  challenge served (no payment header) — traffic signal
 *   [x402-paid] payment verified, handler succeeded — revenue signal
 *   [x402-fail] payment header present but verification failed — security signal
 */
export function withX402MultiNet(
  handler: Handler,
  routeConfig: RouteConfig
): Handler {
  return async (req: NextRequest): Promise<Response> => {
    const startedAt = Date.now();
    const path = safePath(req);
    const minAmount = priceToAtomic(routeConfig.price);

    const verifyResult = await verifyX402Payment(req, {
      minAmount,
      description: routeConfig.description,
      outputSchema: routeConfig.outputSchema,
      inputSchema: routeConfig.inputSchema,
      outputExample: routeConfig.outputExample,
      inputExample: routeConfig.inputExample,
      method: routeConfig.method,
    });

    if (!verifyResult.valid) {
      // Differentiate "no payment header at all" (a 402 challenge) from
      // "payment header present but invalid" (a verification failure).
      const hasPaymentHeader =
        !!(req.headers.get('Payment-Signature') ||
           req.headers.get('PAYMENT-SIGNATURE') ||
           req.headers.get('X-Payment') ||
           req.headers.get('x-payment'));

      if (hasPaymentHeader) {
        console.warn(
          `[x402-fail] path=${path} price=${routeConfig.price} ` +
          `min=${minAmount} ms=${Date.now() - startedAt}`
        );
      } else {
        console.log(
          `[x402-402] path=${path} price=${routeConfig.price} ` +
          `ms=${Date.now() - startedAt}`
        );
      }
      return verifyResult.response;
    }

    // Payment confirmed — call the underlying handler. Annotate request
    // headers with payment metadata so the handler can attribute usage.
    try {
      const response = await handler(req);
      // Add payment-receipt headers (best-effort — don't fail if response is locked)
      try {
        response.headers.set('X-Payment-Verified', 'true');
        response.headers.set('X-Payment-Network', verifyResult.chain);
        response.headers.set('X-Payment-Amount', verifyResult.amountHuman);
        response.headers.set('X-Payment-Signer', verifyResult.signer);
      } catch {
        // Some Response objects have immutable headers; ignore.
      }

      // Structured paid-request log line. Greppable prefix [x402-paid].
      // signer is an on-chain public address — no PII.
      const ms = Date.now() - startedAt;
      console.log(
        `[x402-paid] path=${path} chain=${verifyResult.chain} ` +
        `amount=${verifyResult.amountHuman} signer=${verifyResult.signer} ` +
        `status=${response.status} ms=${ms}`
      );

      // Persist to KV (fire-and-forget, no-op if KV unconfigured).
      recordPaidCall({
        ts: Date.now(),
        path,
        payer: verifyResult.signer,
        price: routeConfig.price,
        priceMicro: priceToMicro(routeConfig.price),
        chain: verifyResult.chain,
        status: response.status,
        ms,
        source: 'multinet',
      });

      return response;
    } catch (err) {
      console.error(
        `[x402-paid-handler-error] path=${path} chain=${verifyResult.chain} ` +
        `signer=${verifyResult.signer} err=${err instanceof Error ? err.message : String(err)}`
      );
      return NextResponse.json(
        { error: 'Internal error after payment', protocol: 'x402' },
        { status: 500 }
      );
    }
  };
}
