/**
 * Calculates extended public keys (xpub/fpub) from a metadata.json file
 * without needing to decrypt any private key shares.
 *
 * Zero external dependencies - uses only Node.js built-ins.
 *
 * Usage: npx ts-node scripts/xpub-from-metadata.ts <path-to-metadata.json>
 *    or: node -e "require('ts-node').register()" scripts/xpub-from-metadata.ts <path>
 */
import { readFileSync } from 'fs';
import { createHash } from 'crypto';

// --- Base58Check encoding (no dependencies) ---

const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';

function base58Encode(buffer: Buffer): string {
  const digits = [0];
  for (const byte of buffer) {
    let carry = byte;
    for (let j = 0; j < digits.length; j++) {
      carry += digits[j] << 8;
      digits[j] = carry % 58;
      carry = (carry / 58) | 0;
    }
    while (carry > 0) {
      digits.push(carry % 58);
      carry = (carry / 58) | 0;
    }
  }

  // Leading zeros
  let result = '';
  for (const byte of buffer) {
    if (byte !== 0) break;
    result += '1';
  }

  for (let i = digits.length - 1; i >= 0; i--) {
    result += BASE58_ALPHABET[digits[i]];
  }

  return result;
}

function base58CheckEncode(payload: Buffer): string {
  const checksum = createHash('sha256').update(
    createHash('sha256').update(payload).digest()
  ).digest().subarray(0, 4);

  return base58Encode(Buffer.concat([payload, checksum]));
}

// --- Types ---

type MetadataKeyEntry = {
  publicKey: string;
  algo: string;
  chainCode?: string;
  cosigners: { id: string; type: string }[];
};

type Metadata = {
  chainCode: string;
  tenantId: string;
  keys?: { [keyId: string]: MetadataKeyEntry };
  keyId?: string;
  publicKey?: string;
};

// --- Extended key encoding ---

const VERSIONS: Record<string, string> = {
  MPC_ECDSA_SECP256K1: '0488B21E',     // xpub
  MPC_CMP_ECDSA_SECP256K1: '0488B21E', // xpub
  MPC_EDDSA_ED25519: '03273e4b',       // fpub
  MPC_CMP_EDDSA_ED25519: '03273e4b',   // fpub
};

function encodeExtendedPubKey(publicKey: string, algo: string, chainCode: Buffer): string {
  const version = VERSIONS[algo];
  if (!version) {
    throw new Error(`Unknown algorithm: ${algo}`);
  }

  // Public keys are always padded to 33 bytes (66 hex chars), matching _encodeKey in reconstructKeys.ts
  const expectedHexLen = 66;
  const paddedKey = publicKey.length < expectedHexLen
    ? '0'.repeat(expectedHexLen - publicKey.length) + publicKey
    : publicKey;

  const payload = Buffer.concat([
    Buffer.from(version, 'hex'),                    // 4 bytes: version
    Buffer.from('000000000000000000', 'hex'),        // 9 bytes: depth + fingerprint + child index
    chainCode,                                       // 32 bytes: chain code
    Buffer.from(paddedKey, 'hex'),                   // public key
  ]);

  return base58CheckEncode(payload);
}

// --- Main ---

function main() {
  const metadataPath = process.argv[2];
  if (!metadataPath) {
    console.error('Usage: npx ts-node scripts/xpub-from-metadata.ts <path-to-metadata.json>');
    process.exit(1);
  }

  const metadata: Metadata = JSON.parse(readFileSync(metadataPath, 'utf-8'));
  const defaultChainCode = Buffer.from(metadata.chainCode, 'hex');

  if (defaultChainCode.length !== 32) {
    throw new Error(`Invalid chain code length: ${defaultChainCode.length} (expected 32)`);
  }

  console.log(`Tenant: ${metadata.tenantId}\n`);

  // Old format: single key
  if (!metadata.keys && metadata.keyId && metadata.publicKey) {
    const extPubKey = encodeExtendedPubKey(metadata.publicKey, 'MPC_ECDSA_SECP256K1', defaultChainCode);
    console.log(`Key ${metadata.keyId} (MPC_ECDSA_SECP256K1):`);
    console.log(`  xpub: ${extPubKey}`);
    return;
  }

  if (!metadata.keys) {
    throw new Error('No keys found in metadata');
  }

  for (const [keyId, entry] of Object.entries(metadata.keys)) {
    const chainCode = entry.chainCode
      ? Buffer.from(entry.chainCode, 'hex')
      : Buffer.from(defaultChainCode);

    if (chainCode.length !== 32) {
      throw new Error(`Invalid chain code length for key ${keyId}: ${chainCode.length}`);
    }

    const extPubKey = encodeExtendedPubKey(entry.publicKey, entry.algo, chainCode);
    const label = entry.algo.includes('ECDSA') ? 'xpub' : 'fpub';

    console.log(`Key ${keyId} (${entry.algo}):`);
    console.log(`  ${label}: ${extPubKey}`);
    console.log();
  }
}

main();
