/**
 * test-key-manager-eoa.ts — Test EOA integration in EncryptIDKeyManager.
 *
 * Verifies that the key manager properly derives EOA keys from PRF output
 * and includes them in the DerivedKeys interface.
 *
 * Usage:
 *   bun run scripts/test-key-manager-eoa.ts
 */

import { EncryptIDKeyManager } from '../src/encryptid/key-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('=== Key Manager EOA Integration Tests ===\n');

  // Simulate PRF output (32 bytes)
  const prfOutput = new ArrayBuffer(32);
  const view = new Uint8Array(prfOutput);
  for (let i = 0; i < 32; i++) view[i] = i + 1;

  // Test 1: Initialize from PRF and derive keys
  console.log('[1] Derive keys from PRF (includes EOA)');
  const km = new EncryptIDKeyManager();
  assert(!km.isInitialized(), 'Not initialized before init');

  await km.initFromPRF(prfOutput);
  assert(km.isInitialized(), 'Initialized after initFromPRF');

  const keys = await km.getKeys();
  assert(keys.fromPRF === true, 'Keys marked as from PRF');
  assert(keys.encryptionKey !== undefined, 'Has encryption key');
  assert(keys.signingKeyPair !== undefined, 'Has signing key pair');
  assert(keys.didSeed !== undefined, 'Has DID seed');
  assert(keys.did !== undefined, 'Has DID');

  // EOA fields should be present (PRF path)
  assert(keys.eoaPrivateKey !== undefined, 'Has EOA private key');
  assert(keys.eoaAddress !== undefined, 'Has EOA address');
  assert(keys.eoaPrivateKey!.length === 32, 'EOA private key is 32 bytes');
  assert(keys.eoaAddress!.startsWith('0x'), 'EOA address starts with 0x');
  assert(keys.eoaAddress!.length === 42, 'EOA address is 42 chars');
  console.log(`  EOA Address: ${keys.eoaAddress}`);

  // Test 2: Keys are cached (same object returned)
  console.log('\n[2] Key caching');
  const keys2 = await km.getKeys();
  assert(keys2.eoaAddress === keys.eoaAddress, 'Cached keys return same EOA address');

  // Test 3: Determinism — new manager with same PRF gives same EOA
  console.log('\n[3] Determinism across key manager instances');
  const km2 = new EncryptIDKeyManager();
  await km2.initFromPRF(prfOutput);
  const keys3 = await km2.getKeys();
  assert(keys3.eoaAddress === keys.eoaAddress, 'Same PRF → same EOA across instances');
  assert(toHex(keys3.eoaPrivateKey!) === toHex(keys.eoaPrivateKey!), 'Same private key too');

  // Test 4: Clear wipes EOA key
  console.log('\n[4] Clear wipes sensitive material');
  const eoaKeyRef = keys.eoaPrivateKey!;
  const eoaBefore = toHex(eoaKeyRef);
  assert(eoaBefore !== '00'.repeat(32), 'EOA key non-zero before clear');
  km.clear();
  assert(!km.isInitialized(), 'Not initialized after clear');
  // The referenced Uint8Array should be zeroed
  const eoaAfter = toHex(eoaKeyRef);
  assert(eoaAfter === '00'.repeat(32), 'EOA private key zeroed after clear');

  // Test 5: Passphrase path — no EOA keys
  console.log('\n[5] Passphrase path (no EOA)');
  const km3 = new EncryptIDKeyManager();
  const salt = new Uint8Array(32);
  crypto.getRandomValues(salt);
  await km3.initFromPassphrase('test-passphrase-123', salt);
  const passKeys = await km3.getKeys();
  assert(passKeys.fromPRF === false, 'Keys marked as from passphrase');
  assert(passKeys.eoaPrivateKey === undefined, 'No EOA private key from passphrase');
  assert(passKeys.eoaAddress === undefined, 'No EOA address from passphrase');
  assert(passKeys.encryptionKey !== undefined, 'Still has encryption key');
  km3.clear();

  // Test 6: Domain separation — EOA address differs from DID-derived values
  console.log('\n[6] Domain separation');
  const km4 = new EncryptIDKeyManager();
  await km4.initFromPRF(prfOutput);
  const keys4 = await km4.getKeys();
  // EOA address should not appear in the DID
  assert(!keys4.did.includes(keys4.eoaAddress!.slice(2)), 'EOA address not in DID');
  km4.clear();

  // Cleanup km2
  km2.clear();

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

main().catch(err => {
  console.error('Fatal:', err);
  process.exit(1);
});
