/**
 * Structured logging helper for x402 POST action handlers.
 *
 * The x402-next SDK's `withX402` wrapper intercepts unpaid requests at the
 * 402-challenge layer and never invokes our handler — so by the time our
 * handler runs, payment has already been verified by the facilitator.
 * That means we can log a clean `[x402-action-paid]` line inside the handler
 * to track real, paid usage.
 *
 * Logs are structured + greppable. No PII — only the public payer address
 * (when present in the request body), the path, the price, and the
 * tx-prepare result status.
 *
 * Usage in a route:
 *   const handler = async (req: NextRequest) => { ... };
 *   export const POST = withX402(withActionLogging(handler, '$0.50', '/actions/register-agent'), ...);
 */

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

// Loose handler shape: takes a NextRequest, returns a NextResponse (any payload).
// We intentionally use NextResponse<unknown> so route handlers that return
// union types like `NextResponse<A> | NextResponse<B>` (e.g. error vs. success
// payloads) flow through the wrapper without TypeScript narrowing complaints.
type Handler = (req: NextRequest) => Promise<NextResponse<unknown>>;

/**
 * Wrap a paid x402 action handler with structured logging. The wrapper is a
 * no-op for the request lifecycle — it just emits one log line per paid call.
 *
 * Greppable prefixes:
 *   [x402-action-paid] — handler ran (payment was already verified by withX402)
 *   [x402-action-fail] — handler threw before responding
 *   [x402-action-4xx]  — handler returned a 4xx (e.g. invalid input after payment)
 */
export function withActionLogging(
  handler: Handler,
  price: string,
  path: string
): Handler {
  return async (req: NextRequest): Promise<NextResponse<unknown>> => {
    const startedAt = Date.now();
    let payerHint: string | undefined;
    try {
      // Best-effort: peek at walletAddress in the body for an attribution hint.
      // We clone the request because reading the body consumes it.
      const cloned = req.clone();
      const body = await cloned.json().catch(() => ({}));
      if (body && typeof body.walletAddress === 'string' && /^0x[a-fA-F0-9]{40}$/.test(body.walletAddress)) {
        payerHint = body.walletAddress;
      }
    } catch {
      // ignore
    }

    try {
      const res = await handler(req);
      const ms = Date.now() - startedAt;
      const status = res.status;
      if (status >= 200 && status < 300) {
        console.log(
          `[x402-action-paid] path=${path} price=${price} ` +
          `payer=${payerHint || 'unknown'} status=${status} ms=${ms}`
        );
        // Persist to KV (fire-and-forget, no-op if KV unconfigured).
        // Action endpoints are POST and use the x402-next withX402 wrapper,
        // which only supports Base — hence chain='base'.
        recordPaidCall({
          ts: Date.now(),
          path,
          payer: payerHint || 'unknown',
          price,
          priceMicro: priceToMicro(price),
          chain: 'base',
          status,
          ms,
          source: 'action',
        });
      } else if (status >= 400 && status < 500) {
        console.warn(
          `[x402-action-4xx] path=${path} price=${price} ` +
          `payer=${payerHint || 'unknown'} status=${status} ms=${ms}`
        );
      } else {
        console.error(
          `[x402-action-5xx] path=${path} price=${price} ` +
          `payer=${payerHint || 'unknown'} status=${status} ms=${ms}`
        );
      }
      return res;
    } catch (err) {
      const ms = Date.now() - startedAt;
      const msg = err instanceof Error ? err.message : String(err);
      console.error(
        `[x402-action-fail] path=${path} price=${price} ` +
        `payer=${payerHint || 'unknown'} err=${msg} ms=${ms}`
      );
      throw err;
    }
  };
}
