/**
 * POST /api/x402/settle
 *
 * Executes an x402 payment settlement by calling USDC.transferFrom on Base mainnet.
 *
 * The relayer (deployer) key is loaded ONLY from process.env.DEPLOYER_PRIVATE_KEY.
 * It is never written to any file, logged, or returned in any response.
 *
 * Flow:
 *   1. Calls /api/x402/verify internally to validate the payment authorization
 *   2. Loads the relayer private key from environment
 *   3. Executes USDC.transferFrom(from, to, amount) using the relayer as the caller
 *   4. Returns the transaction hash and settlement details
 *
 * The USDC ERC-20 transferFrom requires:
 *   - The 'from' address must have approved the relayer address for at least 'amount'
 *   - The relayer calls transferFrom(from, to, amount)
 *   - This is verified by /api/x402/verify before execution
 *
 * Request body:
 *   {
 *     paymentHeader: string       // base64-encoded JSON payment authorization
 *     expectedRecipient: string   // address that should receive the payment
 *     expectedAmount: string      // expected USDC amount in base units (6 decimals)
 *     taskId?: string             // optional task ID for logging
 *   }
 *
 * Response (success):
 *   {
 *     settled: true,
 *     txHash: string,
 *     from: string,
 *     to: string,
 *     amount: string,
 *     amountHuman: string,
 *     blockNumber: string,
 *     explorerUrl: string
 *   }
 *
 * Response (failure):
 *   { settled: false, error: string }
 *
 * Security:
 *   - DEPLOYER_PRIVATE_KEY is ONLY read from process.env — never from files or request body
 *   - The private key is never logged, never returned in responses, never stored
 *   - Rate limiting should be applied at the edge (Vercel middleware or Cloudflare)
 */

import { NextRequest, NextResponse } from 'next/server';
import {
  createPublicClient,
  createWalletClient,
  http,
  parseAbi,
  type Hex,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';

// USDC on Base mainnet
const USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as const;
const USDC_DECIMALS = 6;

const USDC_ABI = parseAbi([
  'function transferFrom(address from, address to, uint256 amount) returns (bool)',
  'function allowance(address owner, address spender) view returns (uint256)',
  'function balanceOf(address account) view returns (uint256)',
]);

const BASE_RPC = 'https://mainnet.base.org';

export async function POST(req: NextRequest) {
  // -------------------------------------------------------------------------
  // 1. Load relayer key from environment — NEVER from request body or files
  // -------------------------------------------------------------------------
  const deployerKey = process.env.DEPLOYER_PRIVATE_KEY;
  if (!deployerKey) {
    console.error('[x402/settle] DEPLOYER_PRIVATE_KEY is not set in environment');
    return NextResponse.json({
      settled: false,
      error: 'Settlement service not configured — DEPLOYER_PRIVATE_KEY missing from environment',
    }, { status: 503 });
  }

  // Normalize key format (ensure 0x prefix)
  const normalizedKey = (deployerKey.startsWith('0x') ? deployerKey : `0x${deployerKey}`) as Hex;

  // -------------------------------------------------------------------------
  // 2. Parse and validate request body
  // -------------------------------------------------------------------------
  let body: {
    paymentHeader: string;
    expectedRecipient?: string;
    expectedAmount?: string;
    taskId?: string;
  };

  try {
    body = await req.json();
  } catch {
    return NextResponse.json({ settled: false, error: 'Invalid JSON body' }, { status: 400 });
  }

  const { paymentHeader, expectedRecipient, expectedAmount, taskId } = body;

  if (!paymentHeader) {
    return NextResponse.json({ settled: false, error: 'Missing paymentHeader' }, { status: 400 });
  }

  // -------------------------------------------------------------------------
  // 3. Decode payment header
  // -------------------------------------------------------------------------
  let paymentData: {
    signature: Hex;
    from: `0x${string}`;
    to: `0x${string}`;
    amount: string;
    nonce: Hex;
    deadline: string;
  };

  try {
    const decoded = Buffer.from(paymentHeader, 'base64').toString('utf-8');
    paymentData = JSON.parse(decoded);
  } catch {
    return NextResponse.json({ settled: false, error: 'Invalid paymentHeader encoding' }, { status: 400 });
  }

  const { from, to, amount, deadline } = paymentData;

  if (!from || !to || !amount || !deadline) {
    return NextResponse.json({ settled: false, error: 'Missing required fields in payment header' }, { status: 400 });
  }

  // -------------------------------------------------------------------------
  // 4. Verify payment authorization before executing
  // -------------------------------------------------------------------------
  const verifyPayload: Record<string, string> = { paymentHeader };
  if (expectedRecipient) verifyPayload.expectedRecipient = expectedRecipient;
  if (expectedAmount) verifyPayload.expectedAmount = expectedAmount;

  const verifyResponse = await fetch(
    new URL('/api/x402/verify', req.url).toString(),
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(verifyPayload),
    }
  );

  const verifyResult = await verifyResponse.json();

  if (!verifyResult.valid) {
    return NextResponse.json({
      settled: false,
      error: `Payment verification failed: ${verifyResult.error}`,
    }, { status: 400 });
  }

  // -------------------------------------------------------------------------
  // 5. Check deadline once more (belt-and-suspenders)
  // -------------------------------------------------------------------------
  const deadlineTs = BigInt(deadline);
  const nowTs = BigInt(Math.floor(Date.now() / 1000));
  if (deadlineTs < nowTs) {
    return NextResponse.json({
      settled: false,
      error: `Payment authorization expired at ${new Date(Number(deadlineTs) * 1000).toISOString()}`,
    }, { status: 400 });
  }

  // -------------------------------------------------------------------------
  // 6. Execute USDC.transferFrom on Base mainnet
  // -------------------------------------------------------------------------
  let account;
  try {
    account = privateKeyToAccount(normalizedKey);
  } catch (err) {
    console.error('[x402/settle] Invalid DEPLOYER_PRIVATE_KEY format');
    return NextResponse.json({
      settled: false,
      error: 'Settlement service configuration error',
    }, { status: 503 });
  }

  const publicClient = createPublicClient({
    chain: base,
    transport: http(BASE_RPC),
  });

  const walletClient = createWalletClient({
    account,
    chain: base,
    transport: http(BASE_RPC),
  });

  const paymentAmount = BigInt(amount);

  try {
    // Simulate first to catch reverts before spending gas
    await publicClient.simulateContract({
      address: USDC_ADDRESS,
      abi: USDC_ABI,
      functionName: 'transferFrom',
      args: [from as `0x${string}`, to as `0x${string}`, paymentAmount],
      account: account.address,
    });
  } catch (simErr) {
    const msg = simErr instanceof Error ? simErr.message : String(simErr);
    console.error('[x402/settle] Simulation failed:', msg);
    return NextResponse.json({
      settled: false,
      error: `Transaction would revert: ${msg}`,
    }, { status: 400 });
  }

  let txHash: Hex;
  try {
    txHash = await walletClient.writeContract({
      address: USDC_ADDRESS,
      abi: USDC_ABI,
      functionName: 'transferFrom',
      args: [from as `0x${string}`, to as `0x${string}`, paymentAmount],
    });
  } catch (txErr) {
    const msg = txErr instanceof Error ? txErr.message : String(txErr);
    console.error('[x402/settle] Transaction failed:', msg);
    return NextResponse.json({
      settled: false,
      error: `Transaction failed: ${msg}`,
    }, { status: 500 });
  }

  // -------------------------------------------------------------------------
  // 7. Wait for receipt and return result
  // -------------------------------------------------------------------------
  let blockNumber: bigint | undefined;
  try {
    const receipt = await publicClient.waitForTransactionReceipt({
      hash: txHash,
      timeout: 30_000,
    });
    blockNumber = receipt.blockNumber;

    if (receipt.status === 'reverted') {
      return NextResponse.json({
        settled: false,
        error: 'Transaction was mined but reverted on-chain',
        txHash,
        explorerUrl: `https://basescan.org/tx/${txHash}`,
      }, { status: 500 });
    }
  } catch {
    // Receipt timeout — tx was submitted, return hash for client to poll
    console.warn('[x402/settle] Receipt timeout for tx:', txHash);
  }

  const humanAmount = (Number(paymentAmount) / 10 ** USDC_DECIMALS).toFixed(6);

  console.log(`[x402/settle] Settled: ${humanAmount} USDC from ${from} to ${to} | tx: ${txHash}${taskId ? ` | task: ${taskId}` : ''}`);

  return NextResponse.json({
    settled: true,
    txHash,
    from,
    to,
    amount: paymentAmount.toString(),
    amountHuman: humanAmount,
    blockNumber: blockNumber?.toString() ?? null,
    explorerUrl: `https://basescan.org/tx/${txHash}`,
    taskId: taskId ?? null,
  });
}
