/**
 * test-eoa-derivation.ts — Verify EOA key derivation from PRF output.
 *
 * Tests:
 *   1. Determinism: same PRF input → same EOA address every time
 *   2. Domain separation: different from what encryption/signing keys would produce
 *   3. Valid Ethereum address format (EIP-55 checksummed)
 *   4. Different PRF inputs → different addresses
 *   5. Private key zeroing works
 *
 * Usage:
 *   bun run scripts/test-eoa-derivation.ts
 */

import { deriveEOAFromPRF, zeroPrivateKey } from '../src/encryptid/eoa-derivation';

let passed = 0;
let failed = 0;

function assert(condition: boolean, msg: string) {
  if (condition) {
    console.log(`  ✓ ${msg}`);
    passed++;
  } else {
    console.error(`  ✗ ${msg}`);
    failed++;
  }
}

function toHex(bytes: Uint8Array): string {
  return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
}

async function main() {
  console.log('=== EOA Key Derivation Tests ===\n');

  // Fixed PRF output (simulates 32 bytes from WebAuthn PRF extension)
  const prfOutput = new Uint8Array(32);
  for (let i = 0; i < 32; i++) prfOutput[i] = i + 1;

  // Test 1: Basic derivation works
  console.log('[1] Basic derivation');
  const result1 = deriveEOAFromPRF(prfOutput);
  assert(result1.privateKey.length === 32, 'Private key is 32 bytes');
  assert(result1.publicKey.length === 65, 'Public key is 65 bytes (uncompressed)');
  assert(result1.publicKey[0] === 0x04, 'Public key starts with 0x04');
  assert(result1.address.startsWith('0x'), 'Address starts with 0x');
  assert(result1.address.length === 42, 'Address is 42 chars (0x + 40 hex)');
  console.log(`  Address: ${result1.address}`);
  console.log(`  PubKey:  0x${toHex(result1.publicKey).slice(0, 20)}...`);

  // Test 2: Determinism — same input always gives same output
  console.log('\n[2] Determinism');
  const result2 = deriveEOAFromPRF(prfOutput);
  assert(result2.address === result1.address, 'Same PRF → same address');
  assert(toHex(result2.privateKey) === toHex(result1.privateKey), 'Same PRF → same private key');

  // Run it 5 more times to be sure
  for (let i = 0; i < 5; i++) {
    const r = deriveEOAFromPRF(prfOutput);
    assert(r.address === result1.address, `Deterministic on iteration ${i + 3}`);
  }

  // Test 3: Different input → different address
  console.log('\n[3] Different inputs produce different addresses');
  const prfOutput2 = new Uint8Array(32);
  for (let i = 0; i < 32; i++) prfOutput2[i] = 32 - i;
  const result3 = deriveEOAFromPRF(prfOutput2);
  assert(result3.address !== result1.address, 'Different PRF → different address');
  console.log(`  Address 1: ${result1.address}`);
  console.log(`  Address 2: ${result3.address}`);

  // Test 4: EIP-55 checksum validity
  console.log('\n[4] EIP-55 checksum format');
  const hasUppercase = /[A-F]/.test(result1.address.slice(2));
  const hasLowercase = /[a-f]/.test(result1.address.slice(2));
  const isAllHex = /^0x[0-9a-fA-F]{40}$/.test(result1.address);
  assert(isAllHex, 'Address is valid hex');
  assert(hasUppercase || hasLowercase, 'Address has mixed case (EIP-55 checksum)');

  // Test 5: Private key zeroing
  console.log('\n[5] Private key zeroing');
  const result4 = deriveEOAFromPRF(prfOutput);
  const keyBefore = toHex(result4.privateKey);
  assert(keyBefore !== '00'.repeat(32), 'Key is non-zero before wipe');
  zeroPrivateKey(result4.privateKey);
  const keyAfter = toHex(result4.privateKey);
  assert(keyAfter === '00'.repeat(32), 'Key is all zeros after wipe');

  // Test 6: Domain separation — verify the address is different from what
  // you'd get with different HKDF params (we can't test WebCrypto HKDF here
  // but we can verify our specific salt/info combo produces a unique result)
  console.log('\n[6] Domain separation');
  // Manually derive with same input but different params using @noble/hashes
  const { hkdf } = await import('@noble/hashes/hkdf');
  const { sha256 } = await import('@noble/hashes/sha256');
  const encoder = new TextEncoder();
  const encryptionKeyBytes = hkdf(sha256, prfOutput, encoder.encode('encryptid-encryption-key-v1'), encoder.encode('AES-256-GCM'), 32);
  const signingKeyBytes = hkdf(sha256, prfOutput, encoder.encode('encryptid-signing-key-v1'), encoder.encode('ECDSA-P256-seed'), 32);
  const eoaKeyBytes = hkdf(sha256, prfOutput, encoder.encode('encryptid-eoa-key-v1'), encoder.encode('secp256k1-signing-key'), 32);

  assert(toHex(encryptionKeyBytes) !== toHex(eoaKeyBytes), 'EOA key ≠ encryption key material');
  assert(toHex(signingKeyBytes) !== toHex(eoaKeyBytes), 'EOA key ≠ signing key material');
  assert(toHex(result1.privateKey) === toHex(eoaKeyBytes), 'EOA key matches expected HKDF output');

  // Summary
  console.log(`\n=== Results: ${passed} passed, ${failed} failed ===`);
  process.exit(failed > 0 ? 1 : 0);
}

main();
