import { type Stage, ok } from '../types';
import { finalize, refund } from '../../services/escrow.service';
import { logger } from '../../config/logger';
import { settleX402 } from './x402-settle';

/**
 * ESCROW_FINALIZE stage (§12.43 stage 10, §12.151).
 * Finalize escrow: charge on success, refund on failure.
 * Combined with LEDGER_WRITE in one PG transaction (§12.151).
 * Skip on cache hit (no escrow to finalize).
 */
export const escrowFinalizeStage: Stage = {
  name: 'ESCROW_FINALIZE',

  async execute(ctx) {
    // Something to serve: a successful provider call OR a cache hit being replayed.
    // Both are "agent got their data" — payment should settle in either case.
    const hasResponseToServe = (ctx.providerCalled && ctx.providerResponse) || ctx.cacheHit;

    // x402 on-chain payment — settle with facilitator (§8.9).
    // Cache-hit path added 2026-04-22: anonymous x402 agents with balance=0 need
    // their signed payment to cover cache-hit billing (instead of dead balance debit).
    if (ctx.x402Paid && ctx.x402PaymentHeader && hasResponseToServe) {
      await settleX402(ctx);
      ctx.billingStatus = 'PAID';
      ctx.finalCost = ctx.toolPrice ?? 0;
      return ok(ctx);
    }

    // MPP on-chain payment — already settled at Tempo verification time (no facilitator).
    // Cache-hit covered by hasResponseToServe same as x402.
    if (ctx.mppPaid && hasResponseToServe) {
      ctx.billingStatus = 'PAID';
      ctx.finalCost = ctx.toolPrice ?? 0;
      return ok(ctx);
    }

    // Cache hit without payment rail — fall through to LEDGER_WRITE (balance debit, §12.173)
    if (ctx.cacheHit) {
      return ok(ctx);
    }

    // No escrow reserved (free tool or error before escrow)
    if (!ctx.escrowId || !ctx.escrowCreatedAt) {
      return ok(ctx);
    }

    // Provider succeeded → finalize (charge)
    if (ctx.providerCalled && ctx.providerResponse) {
      const updated = await finalize(ctx.escrowId, ctx.escrowCreatedAt, ctx.providerDurationMs);

      ctx.billingStatus = 'PAID';
      ctx.finalCost = ctx.escrowAmount ?? 0;

      if (updated === 0) {
        logger.warn(
          { executionId: ctx.escrowId, requestId: ctx.requestId },
          'Escrow already finalized (idempotent)',
        );
      }

      return ok(ctx);
    }

    // Provider failed or not called → refund
    if (ctx.agentId && ctx.escrowAmount) {
      await refund(ctx.escrowId, ctx.escrowCreatedAt, ctx.agentId, ctx.escrowAmount);
    }

    ctx.billingStatus = 'REFUNDED';
    ctx.finalCost = 0;

    return ok(ctx);
  },
};
