/**
 * Live on-chain tests for @vaultfire/x402-guard.
 * Hits the real Base RPC and the real Vaultfire identity registry.
 *
 * Run: npm test
 */

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

import { VaultfireGuard, checkTrust, VaultfireGuardError } from '../src/index.js';

// Tiny pause between live RPC tests so we don't burst the Base endpoint.
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

// Use a more generous public Base RPC for the test suite. The default
// mainnet.base.org rate-limits aggressively when several reads are issued
// back-to-back. Override via BASE_RPC_URL env var if you have your own node.
const BASE_RPC = process.env.BASE_RPC_URL ?? 'https://base.publicnode.com';
const rpcUrls = { base: BASE_RPC } as const;

// Known on-chain state (verified 2026-04-29):
// - 0xfA15Ee28939B222B0448261A22156070f0A7813C is registered + active on Base
// - 0x000000000000000000000000000000000000dEaD has no Vaultfire identity
const REGISTERED_AGENT = '0xfA15Ee28939B222B0448261A22156070f0A7813C';
const UNREGISTERED_BURN = '0x000000000000000000000000000000000000dEaD';

describe('@vaultfire/x402-guard (live on-chain)', { concurrency: false }, () => {

it('checkTrust returns a decision for a registered agent on Base', async () => {
  const decision = await checkTrust(REGISTERED_AGENT, {
    chains: ['base'],
    minScore: 0, // accept any score so we just verify the registration path works
    rpcUrls,
  });
  assert.equal(decision.address, REGISTERED_AGENT);
  assert.equal(decision.chain, 'base');
  assert.ok(decision.verification, 'verification object should be present');
  assert.equal(typeof decision.score, 'number');
  assert.ok(decision.score >= 0 && decision.score <= 95, 'score in range 0..95');
  await sleep(400);
});

it('VaultfireGuard.check denies an unregistered burn address', async () => {
  const guard = new VaultfireGuard({
    chains: ['base'],
    minScore: 20,
    rpcUrls,
  });
  const decision = await guard.check(UNREGISTERED_BURN);
  assert.equal(decision.allow, false);
  assert.match(decision.reason, /no Vaultfire identity|below minimum/i);
  await sleep(400);
});

it('VaultfireGuard.check rejects malformed addresses', async () => {
  const guard = new VaultfireGuard({ chains: ['base'], rpcUrls });
  const decision = await guard.check('not-an-address');
  assert.equal(decision.allow, false);
  assert.match(decision.reason, /Invalid address/);
});

it('warnOnly mode allows even when below threshold', async () => {
  const guard = new VaultfireGuard({
    chains: ['base'],
    minScore: 95, // unattainable, force a "deny" path
    warnOnly: true,
    rpcUrls,
  });
  const decision = await guard.check(REGISTERED_AGENT);
  assert.equal(decision.allow, true);
  assert.match(decision.reason, /\[warn-only\]/);
  await sleep(400);
});

it('allowUnregistered=true permits payments to unregistered counterparties', async () => {
  const guard = new VaultfireGuard({
    chains: ['base'],
    allowUnregistered: true,
    rpcUrls,
  });
  const decision = await guard.check(UNREGISTERED_BURN);
  assert.equal(decision.allow, true);
  assert.match(decision.reason, /not registered.*allowUnregistered/);
  await sleep(400);
});

it('VaultfireGuardError carries the decision', async () => {
  const guard = new VaultfireGuard({ chains: ['base'], minScore: 95, rpcUrls });
  const decision = await guard.check(REGISTERED_AGENT);
  if (!decision.allow) {
    const err = new VaultfireGuardError('blocked', decision);
    assert.equal(err.decision.allow, false);
    assert.equal(err.name, 'VaultfireGuardError');
  }
});

});  // end describe
