// ============================================================
// AES-256-GCM helpers for application-layer encryption of PHI
// ============================================================
// Reads DATA_ENCRYPTION_KEY from env (hex, 32 bytes / 64 hex chars).
// Generate one with:  openssl rand -hex 32
// ============================================================
var crypto = require('crypto');

var KEY = null;
var raw = process.env.DATA_ENCRYPTION_KEY;
if (raw) {
  if (raw.length === 64 && /^[0-9a-fA-F]+$/.test(raw)) {
    KEY = Buffer.from(raw, 'hex');
  } else if (raw.length >= 32) {
    // Fallback: derive a 32-byte key from any string via SHA-256.
    KEY = crypto.createHash('sha256').update(raw).digest();
    console.warn('[crypto] DATA_ENCRYPTION_KEY is not 64 hex chars; derived via SHA-256.');
  }
}

if (!KEY && (process.env.NODE_ENV === 'production' || process.env.APP_URL)) {
  console.error('[FATAL] DATA_ENCRYPTION_KEY is not set. Refusing to run in production.');
  process.exit(1);
}

// Ciphertext format:  "enc1:" + base64( iv(12) || authTag(16) || ciphertext )
var PREFIX = 'enc1:';

function isEncrypted(value) {
  return typeof value === 'string' && value.indexOf(PREFIX) === 0;
}

function encryptString(plaintext) {
  if (plaintext == null) return plaintext;
  if (!KEY) return plaintext; // dev mode: passthrough
  var iv = crypto.randomBytes(12);
  var cipher = crypto.createCipheriv('aes-256-gcm', KEY, iv);
  var ct = Buffer.concat([cipher.update(String(plaintext), 'utf8'), cipher.final()]);
  var tag = cipher.getAuthTag();
  return PREFIX + Buffer.concat([iv, tag, ct]).toString('base64');
}

function decryptString(value) {
  if (value == null) return value;
  if (!isEncrypted(value)) return value; // plaintext (legacy row)
  if (!KEY) throw new Error('Cannot decrypt: DATA_ENCRYPTION_KEY not set');
  var buf = Buffer.from(String(value).slice(PREFIX.length), 'base64');
  var iv = buf.subarray(0, 12);
  var tag = buf.subarray(12, 28);
  var ct = buf.subarray(28);
  var decipher = crypto.createDecipheriv('aes-256-gcm', KEY, iv);
  decipher.setAuthTag(tag);
  return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
}

function encryptBuffer(buf) {
  if (!buf) return buf;
  if (!KEY) return buf;
  var iv = crypto.randomBytes(12);
  var cipher = crypto.createCipheriv('aes-256-gcm', KEY, iv);
  var ct = Buffer.concat([cipher.update(buf), cipher.final()]);
  var tag = cipher.getAuthTag();
  // Version byte (0x01) + iv(12) + tag(16) + ciphertext
  return Buffer.concat([Buffer.from([0x01]), iv, tag, ct]);
}

function isEncryptedBuffer(buf) {
  return Buffer.isBuffer(buf) && buf.length > 29 && buf[0] === 0x01;
}

function decryptBuffer(buf) {
  if (!Buffer.isBuffer(buf)) return buf;
  if (!isEncryptedBuffer(buf)) return buf; // legacy (gzip without enc wrapper)
  if (!KEY) throw new Error('Cannot decrypt buffer: DATA_ENCRYPTION_KEY not set');
  var iv = buf.subarray(1, 13);
  var tag = buf.subarray(13, 29);
  var ct = buf.subarray(29);
  var decipher = crypto.createDecipheriv('aes-256-gcm', KEY, iv);
  decipher.setAuthTag(tag);
  return Buffer.concat([decipher.update(ct), decipher.final()]);
}

module.exports = {
  encryptString: encryptString,
  decryptString: decryptString,
  isEncrypted: isEncrypted,
  encryptBuffer: encryptBuffer,
  decryptBuffer: decryptBuffer,
  isEncryptedBuffer: isEncryptedBuffer,
  hasKey: function() { return !!KEY; }
};
