/**
 * AES-256-GCM encryption helpers for Fastmail app-password storage.
 *
 * Security requirements (T-03-01, T-03-03, ASVS V6):
 *   - Key from APP_PASSWORD_ENCRYPTION_KEY env (64-char hex = 32 bytes)
 *   - 96-bit (12-byte) random IV per encryption — never reuse IV
 *   - GCM auth tag verifies integrity on decrypt; tampered ciphertext throws
 *   - Stored payload: JSON { iv, authTag, ciphertext } (all hex)
 *   - Never log plaintext or the encryption key
 *
 * Source: Node.js docs node:crypto — AES-GCM
 */

import { randomBytes, createCipheriv, createDecipheriv } from 'node:crypto';

// Validated at module load: key must be present and 32 bytes (64 hex chars).
// We deliberately do NOT throw here on missing key — that would crash the module
// during tests that set the env before the first import. The KEY is only read
// when encryptPassword / decryptPassword are actually called.
function getKey(): Buffer {
  const hex = process.env.APP_PASSWORD_ENCRYPTION_KEY;
  if (!hex || hex.length !== 64) {
    throw new Error(
      'APP_PASSWORD_ENCRYPTION_KEY must be a 64-character hex string (32 bytes). ' +
        "Generate with: node -e \"console.log(require('crypto').randomBytes(32).toString('hex'))\"",
    );
  }
  return Buffer.from(hex, 'hex');
}

interface EncryptedPayload {
  iv: string;
  authTag: string;
  ciphertext: string;
}

/**
 * Encrypts a plaintext string using AES-256-GCM.
 * Returns a JSON string with { iv, authTag, ciphertext } — all hex-encoded.
 * Each call generates a fresh random 96-bit IV.
 */
export function encryptPassword(plaintext: string): string {
  const key = getKey();
  const iv = randomBytes(12); // 96-bit IV for GCM
  const cipher = createCipheriv('aes-256-gcm', key, iv);
  const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
  const authTag = cipher.getAuthTag();
  const payload: EncryptedPayload = {
    iv: iv.toString('hex'),
    authTag: authTag.toString('hex'),
    ciphertext: encrypted.toString('hex'),
  };
  return JSON.stringify(payload);
}

/**
 * Decrypts an AES-256-GCM encrypted payload produced by encryptPassword.
 * Throws if the auth tag does not match (integrity violation).
 */
export function decryptPassword(stored: string): string {
  const key = getKey();
  const { iv, authTag, ciphertext } = JSON.parse(stored) as EncryptedPayload;
  const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(iv, 'hex'));
  decipher.setAuthTag(Buffer.from(authTag, 'hex'));
  return Buffer.concat([
    decipher.update(Buffer.from(ciphertext, 'hex')),
    decipher.final(),
  ]).toString('utf8');
}
