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

// AES-256-GCM at-rest encryption for vault secrets (the brokered Vaultwarden token set + the Officer-app
// protector key). The whole point of the vault store is that a DB dump must not hand over the keys to the
// vault, so these columns are never stored plaintext.
//
// Key = SHA-256(VAULT_STORE_KEY) so any sufficiently strong secret works (mirrors the JWT_SECRET style).
// Format = base64(iv[12] | authTag[16] | ciphertext). The key is read LAZILY so the platform still boots
// without a vault configured — vault storage ops then throw a clear error instead of crashing startup.

let cachedKey: Buffer | null = null;
function key(): Buffer {
  if (cachedKey) return cachedKey;
  const secret = process.env.VAULT_STORE_KEY;
  if (!secret || secret.length < 16) {
    throw new Error('VAULT_STORE_KEY must be set (>=16 chars) to store vault secrets');
  }
  cachedKey = createHash('sha256').update(secret).digest();
  return cachedKey;
}

/** Encrypt a UTF-8 secret for at-rest storage → base64(iv|tag|ciphertext). */
export function encryptSecret(plaintext: string): string {
  const iv = randomBytes(12);
  const cipher = createCipheriv('aes-256-gcm', key(), iv);
  const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();
  return Buffer.concat([iv, tag, ct]).toString('base64');
}

/** Decrypt a value produced by encryptSecret. Throws if the ciphertext/tag/key don't verify. */
export function decryptSecret(blob: string): string {
  const buf = Buffer.from(blob, 'base64');
  const iv = buf.subarray(0, 12);
  const tag = buf.subarray(12, 28);
  const ct = buf.subarray(28);
  const decipher = createDecipheriv('aes-256-gcm', key(), iv);
  decipher.setAuthTag(tag);
  return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
}
