import * as crypto from 'crypto';

/**
 * Decode a Base32 string to a Buffer
 * @param base32 - The Base32 encoded string
 * @returns A Buffer containing the decoded data
 */
function base32Decode(base32: string): Buffer {
  const base32Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
  let bits = 0;
  let value = 0;
  let output = [];

  for (let i = 0; i < base32.length; i++) {
    const idx = base32Chars.indexOf(base32[i].toUpperCase());
    if (idx === -1) continue;

    value = (value << 5) | idx;
    bits += 5;

    if (bits >= 8) {
      output.push((value >>> (bits - 8)) & 0xff);
      bits -= 8;
    }
  }

  return Buffer.from(output);
}

/**
 * Generate a 6-digit TOTP code from a secret key
 * @param secret - The TOTP secret key (Base32 encoded)
 * @returns A 6-digit TOTP code as a string
 */
export function generateTOTP(secret: string): string {
  // Decode the Base32 secret
  const key = base32Decode(secret);

  // Calculate time counter (Unix timestamp / 30 seconds)
  const time = Math.floor(Date.now() / 1000 / 30);
  const timeBuffer = Buffer.alloc(8);
  timeBuffer.writeBigUInt64BE(BigInt(time), 0);

  // Generate HMAC-SHA1 hash
  const hmac = crypto.createHmac('sha1', key);
  hmac.update(timeBuffer);
  const hash = hmac.digest();

  // Extract 6-digit code using dynamic truncation
  const offset = hash[hash.length - 1] & 0x0f;
  const code =
    ((hash[offset] & 0x7f) << 24) |
    ((hash[offset + 1] & 0xff) << 16) |
    ((hash[offset + 2] & 0xff) << 8) |
    (hash[offset + 3] & 0xff);

  // Return 6-digit code as string with leading zeros
  return (code % 1000000).toString().padStart(6, '0');
}
