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

/**
 * AES-256-GCM for secrets at rest (2026-09-22) — TOTP secrets in
 * auth_accounts. Key derived from AUTH_SECRET so no second secret to manage;
 * rotating AUTH_SECRET therefore requires re-enrolling MFA (documented).
 * Format: v1.<iv b64>.<tag b64>.<ciphertext b64>
 */
function key(): Buffer {
  const secret = process.env.AUTH_SECRET
  if (!secret || secret.length < 16) throw new Error('AUTH_SECRET must be set to a random string of at least 16 characters.')
  return createHash('sha256').update(`${secret}:secrets-at-rest:v1`).digest()
}

export function encryptSecret(plain: string): string {
  const iv = randomBytes(12)
  const cipher = createCipheriv('aes-256-gcm', key(), iv)
  const ct = Buffer.concat([cipher.update(plain, 'utf8'), cipher.final()])
  return ['v1', iv.toString('base64'), cipher.getAuthTag().toString('base64'), ct.toString('base64')].join('.')
}

export function decryptSecret(packed: string): string {
  const [v, iv, tag, ct] = packed.split('.')
  if (v !== 'v1' || !iv || !tag || !ct) throw new Error('Unrecognised secret format.')
  const decipher = createDecipheriv('aes-256-gcm', key(), Buffer.from(iv, 'base64'))
  decipher.setAuthTag(Buffer.from(tag, 'base64'))
  return Buffer.concat([decipher.update(Buffer.from(ct, 'base64')), decipher.final()]).toString('utf8')
}
