/**
 * Live tests for the batch-settlement helpers.
 *
 * Pure helpers (constants, withdrawDelay) run offline. checkChannel and
 * verifyVoucherCounterparty hit real Base RPC against known on-chain state.
 */

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';

import {
  BATCH_SETTLEMENT_DOMAIN,
  BATCH_SETTLEMENT_ADDRESS,
  ERC3009_DEPOSIT_COLLECTOR_ADDRESS,
  PERMIT2_DEPOSIT_COLLECTOR_ADDRESS,
  MIN_WITHDRAW_DELAY_SECONDS,
  MAX_WITHDRAW_DELAY_SECONDS,
  isValidWithdrawDelay,
  checkChannel,
  verifyVoucherCounterparty,
  _clearVoucherCache,
} from '../src/batch-settlement.js';

const BASE_RPC = process.env.BASE_RPC_URL ?? 'https://base.publicnode.com';
const rpcUrls = { base: BASE_RPC } as const;

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

// Known on-chain state (verified 2026-04-29 and re-verified 2026-05-12):
//   0xfA15Ee28939B222B0448261A22156070f0A7813C — registered + active on Base
//   0x000000000000000000000000000000000000dEaD — unregistered burn address
const REGISTERED_AGENT = '0xfA15Ee28939B222B0448261A22156070f0A7813C';
const UNREGISTERED_BURN = '0x000000000000000000000000000000000000dEaD';
const USDC_BASE = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';

describe('@vaultfire/x402-guard/batch-settlement (offline pure helpers)', () => {
  it('exports verified x402 batch-settlement constants', () => {
    assert.equal(BATCH_SETTLEMENT_DOMAIN.name, 'x402 Batch Settlement');
    assert.equal(BATCH_SETTLEMENT_DOMAIN.version, '1');
    assert.equal(
      BATCH_SETTLEMENT_ADDRESS,
      '0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003'
    );
    assert.equal(
      ERC3009_DEPOSIT_COLLECTOR_ADDRESS,
      '0x4020806089470a89826cB9fB1f4059150b550004'
    );
    assert.equal(
      PERMIT2_DEPOSIT_COLLECTOR_ADDRESS,
      '0x4020425FAf3B746C082C2f942b4E5159887B0005'
    );
    assert.equal(MIN_WITHDRAW_DELAY_SECONDS, 900);
    assert.equal(MAX_WITHDRAW_DELAY_SECONDS, 2_592_000);
  });

  it('isValidWithdrawDelay accepts the bounds and rejects outside', () => {
    assert.equal(isValidWithdrawDelay(900), true);
    assert.equal(isValidWithdrawDelay(2_592_000), true);
    assert.equal(isValidWithdrawDelay(1800), true);
    assert.equal(isValidWithdrawDelay(899), false);
    assert.equal(isValidWithdrawDelay(2_592_001), false);
    assert.equal(isValidWithdrawDelay(0), false);
    assert.equal(isValidWithdrawDelay(-1), false);
    assert.equal(isValidWithdrawDelay(1.5), false);
  });
});

describe('@vaultfire/x402-guard/batch-settlement (live on-chain)', { concurrency: false }, () => {
  it('checkChannel declines a channel with invalid withdrawDelay before any RPC', async () => {
    const decision = await checkChannel(
      {
        payer: REGISTERED_AGENT,
        payerAuthorizer: REGISTERED_AGENT,
        receiver: REGISTERED_AGENT,
        receiverAuthorizer: REGISTERED_AGENT,
        token: USDC_BASE,
        withdrawDelay: 60, // way below the 900s minimum
        salt: '0x0000000000000000000000000000000000000000000000000000000000000001',
      },
      { chains: ['base'], minScore: 0, rpcUrls }
    );
    assert.equal(decision.allow, false);
    assert.equal(decision.recommendation, 'decline');
    assert.equal(decision.withdrawDelayOk, false);
    await sleep(400);
  });

  it('checkChannel declines when receiverAuthorizer is unregistered', async () => {
    const decision = await checkChannel(
      {
        payer: REGISTERED_AGENT,
        payerAuthorizer: REGISTERED_AGENT,
        receiver: UNREGISTERED_BURN,
        receiverAuthorizer: UNREGISTERED_BURN,
        token: USDC_BASE,
        withdrawDelay: 900,
        salt: '0x0000000000000000000000000000000000000000000000000000000000000002',
      },
      { chains: ['base'], minScore: 20, rpcUrls }
    );
    assert.equal(decision.allow, false);
    // payer is fine, receiver is not → single-payment-only
    assert.equal(decision.recommendation, 'single-payment-only');
    assert.equal(decision.payerTrust.allow, true);
    assert.equal(decision.receiverTrust.allow, false);
    await sleep(400);
  });

  it('verifyVoucherCounterparty rejects unregistered authorizer', async () => {
    _clearVoucherCache();
    const result = await verifyVoucherCounterparty(UNREGISTERED_BURN, {
      chains: ['base'],
      minScore: 20,
      rpcUrls,
    });
    assert.equal(result.allow, false);
    assert.equal(result.cached, false);
    await sleep(400);
  });

  it('verifyVoucherCounterparty caches successive lookups', async () => {
    _clearVoucherCache();
    const first = await verifyVoucherCounterparty(REGISTERED_AGENT, {
      chains: ['base'],
      minScore: 0,
      rpcUrls,
    });
    assert.equal(first.cached, false);

    const second = await verifyVoucherCounterparty(REGISTERED_AGENT, {
      chains: ['base'],
      minScore: 0,
      rpcUrls,
    });
    assert.equal(second.cached, true);
    assert.ok(second.cacheAgeMs >= 0);
    await sleep(400);
  });
});
