/**
 * POST /api/x402/settle
 *
 * Executes an x402 payment settlement by calling USDC.transferFrom on the target chain.
 *
 * Multi-chain support: Base, Avalanche, Arbitrum, Polygon.
 * The chain is determined by the `chainId` field in the request body,
 * defaulting to Base (8453) for backwards compatibility.
 *
 * 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
 *
 * Supported Chains:
 *   Base (8453)      — USDC: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
 *   Avalanche (43114) — USDC: 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E
 *   Arbitrum (42161)  — USDC: 0xaf88d065e77c8cC2239327C5EDb3A432268e5831
 *   Polygon (137)     — USDC: 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359
 *
 * 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)
 *     chainId?: number            // chain ID (default: 8453 = Base)
 *     taskId?: string             // optional task ID for logging
 *   }
 *
 * Response (success):
 *   {
 *     settled: true,
 *     txHash: string,
 *     from: string,
 *     to: string,
 *     amount: string,
 *     amountHuman: string,
 *     blockNumber: string,
 *     chain: string,
 *     chainId: number,
 *     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 { x402ReleaseGateClosed } from '../_lib/release-gate';
import {
  createPublicClient,
  createWalletClient,
  http,
  parseAbi,
  type Hex,
  type Chain,
} from 'viem';
import { checkRateLimit, RATE_LIMIT_WRITE } from '../../../lib/rate-limit';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import { avalanche } from 'viem/chains';
import { arbitrum } from 'viem/chains';
import { polygon } from 'viem/chains';

// ---------------------------------------------------------------------------
// Chain Configuration
// ---------------------------------------------------------------------------

interface ChainConfig {
  chain: Chain;
  usdc: `0x${string}`;
  rpc: string;
  name: string;
  explorer: string;
}

const CHAIN_CONFIGS: Record<number, ChainConfig> = {
  8453: {
    chain: base,
    usdc: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
    rpc: process.env.BASE_RPC_URL || 'https://mainnet.base.org',
    name: 'Base',
    explorer: 'https://basescan.org',
  },
  43114: {
    chain: avalanche,
    usdc: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E',
    rpc: process.env.AVALANCHE_RPC_URL || 'https://api.avax.network/ext/bc/C/rpc',
    name: 'Avalanche',
    explorer: 'https://snowtrace.io',
  },
  42161: {
    chain: arbitrum,
    usdc: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
    rpc: process.env.ARBITRUM_RPC_URL || 'https://arb1.arbitrum.io/rpc',
    name: 'Arbitrum',
    explorer: 'https://arbiscan.io',
  },
  137: {
    chain: polygon,
    usdc: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359',
    rpc: process.env.POLYGON_RPC_URL || 'https://polygon-bor-rpc.publicnode.com',
    name: 'Polygon',
    explorer: 'https://polygonscan.com',
  },
};

const DEFAULT_CHAIN_ID = 8453;
const USDC_DECIMALS = 6;

// ---------------------------------------------------------------------------
// VF-03: Nonce deduplication store — prevents settlement replay attacks
// Settle maintains its own store separate from verify so settled nonces are
// permanently tracked even if the verify store is evicted or rotated.
// ---------------------------------------------------------------------------
interface SettlementNonceEntry {
  seenAt: number;
}

const settledNonces = new Map<string, SettlementNonceEntry>();
const SETTLE_NONCE_TTL_MS = 60 * 60 * 1000; // 1 hour

function cleanupSettledNonces(): void {
  const cutoff = Date.now() - SETTLE_NONCE_TTL_MS;
  for (const [key, entry] of settledNonces) {
    if (entry.seenAt < cutoff) settledNonces.delete(key);
  }
}

function hasSettlementNonceBeenUsed(nonce: string, chainId: number): boolean {
  cleanupSettledNonces();
  return settledNonces.has(`${chainId}:${nonce}`);
}

function markSettlementNonceUsed(nonce: string, chainId: number): void {
  settledNonces.set(`${chainId}:${nonce}`, { seenAt: Date.now() });
}

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)',
]);

export async function POST(req: NextRequest) {
  return x402ReleaseGateClosed();

  /* Release gate intentionally makes the legacy implementation unreachable.
     Keep it below only for a separately reviewed replacement/migration. */
  // VF-02: Rate limiting — 20 POST requests per minute per IP
  const rateLimitResponse = checkRateLimit(req, RATE_LIMIT_WRITE);
  if (rateLimitResponse) return rateLimitResponse;

  // -------------------------------------------------------------------------
  // 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;
    chainId?: number;
    taskId?: string;
  };

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

  const { paymentHeader, expectedRecipient, expectedAmount, chainId: requestedChainId, taskId } = body;

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

  // Resolve chain configuration
  const chainId = requestedChainId && CHAIN_CONFIGS[requestedChainId]
    ? requestedChainId
    : DEFAULT_CHAIN_ID;
  const chainConfig = CHAIN_CONFIGS[chainId];

  // -------------------------------------------------------------------------
  // 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, nonce } = paymentData;

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

  // VF-03: Nonce replay check — prevent the same payment from being settled twice
  if (nonce && hasSettlementNonceBeenUsed(nonce as string, chainId)) {
    return NextResponse.json({
      settled: false,
      error: 'Nonce has already been used for settlement. This payment has already been processed.',
    }, { status: 400 });
  }

  // -------------------------------------------------------------------------
  // 4. Verify payment authorization before executing (chain-aware)
  // -------------------------------------------------------------------------
  const verifyPayload: Record<string, string | number> = { paymentHeader, chainId };
  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 the target chain
  // -------------------------------------------------------------------------
  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: chainConfig.chain,
    transport: http(chainConfig.rpc),
  });

  const walletClient = createWalletClient({
    account,
    chain: chainConfig.chain,
    transport: http(chainConfig.rpc),
  });

  const paymentAmount = BigInt(amount);

  try {
    // Simulate first to catch reverts before spending gas
    await publicClient.simulateContract({
      address: chainConfig.usdc,
      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 on ${chainConfig.name}:`, msg);
    return NextResponse.json({
      settled: false,
      error: `Transaction simulation failed on ${chainConfig.name}. Check token balance and allowance.`,
    }, { status: 400 });
  }

  let txHash: Hex;
  try {
    // viem's overloaded writeContract types narrow on chain/account; we already
    // configured the wallet client with both, so this call is sound. Cast through
    // `any` to avoid an over-eager type intersection that fights our generic ABI.
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    txHash = await (walletClient.writeContract as any)({
      address: chainConfig.usdc,
      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 on ${chainConfig.name}:`, msg);
    return NextResponse.json({
      settled: false,
      error: `Transaction failed on ${chainConfig.name}. The settlement could not be completed.`,
    }, { 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 on ${chainConfig.name}`,
        txHash,
        explorerUrl: `${chainConfig.explorer}/tx/${txHash}`,
      }, { status: 500 });
    }
  } catch {
    // Receipt timeout — tx was submitted, return hash for client to poll
    console.warn(`[x402/settle] Receipt timeout for tx on ${chainConfig.name}:`, txHash);
  }

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

  // VF-03: Mark the nonce as settled so it cannot be replayed
  if (nonce) markSettlementNonceUsed(nonce as string, chainId);

  console.log(`[x402/settle] Settled on ${chainConfig.name}: ${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,
    chain: chainConfig.name,
    chainId,
    explorerUrl: `${chainConfig.explorer}/tx/${txHash}`,
    taskId: taskId ?? null,
  });
}
