import crypto from 'node:crypto';
import fs from 'node:fs';

export interface EncryptedPayload {
  iv: string;         // base64
  tag: string;        // base64
  ciphertext: string; // base64
}

/**
 * Encrypt plaintext using AES-256-GCM
 */
export function encryptAES256GCM(plaintext: string, key: Buffer): EncryptedPayload {
  if (key.length !== 32) {
    throw new Error(`Master key must be exactly 32 bytes (received ${key.length})`);
  }

  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();

  return {
    iv: iv.toString('base64'),
    tag: tag.toString('base64'),
    ciphertext: encrypted.toString('base64'),
  };
}

/**
 * Decrypt AES-256-GCM ciphertext and verify authentication tag
 */
export function decryptAES256GCM(payload: EncryptedPayload, key: Buffer): string {
  if (key.length !== 32) {
    throw new Error(`Master key must be exactly 32 bytes (received ${key.length})`);
  }

  const iv = Buffer.from(payload.iv, 'base64');
  const tag = Buffer.from(payload.tag, 'base64');
  const ciphertext = Buffer.from(payload.ciphertext, 'base64');

  if (iv.length !== 12) {
    throw new Error('Invalid IV length for AES-GCM (must be 12 bytes)');
  }
  if (tag.length !== 16) {
    throw new Error('Invalid auth tag length for AES-GCM (must be 16 bytes)');
  }

  const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
  decipher.setAuthTag(tag);

  const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
  return decrypted.toString('utf8');
}

/**
 * Resolves the 32-byte master key from environment variable or Docker secret file.
 * Refuses to start if key is missing or invalid.
 */
export function resolveMasterKey(
  envVar = process.env.ZYPHON_MASTER_KEY,
  secretPath = '/run/secrets/zyphon_master_key',
): Buffer {
  let raw: string | undefined = envVar;

  if (!raw && fs.existsSync(secretPath)) {
    try {
      raw = fs.readFileSync(secretPath, 'utf8').trim();
    } catch {
      // ignore
    }
  }

  if (!raw) {
    throw new Error(
      'CRITICAL: Master key not found! Please set ZYPHON_MASTER_KEY (hex or raw 32 bytes) or provide secret at /run/secrets/zyphon_master_key',
    );
  }

  raw = raw.trim();

  // If 64 hex characters (32 bytes)
  if (/^[0-9a-fA-F]{64}$/.test(raw)) {
    return Buffer.from(raw, 'hex');
  }

  // If raw string is 32 bytes
  const buf = Buffer.from(raw, 'utf8');
  if (buf.length === 32) {
    return buf;
  }

  // If base64 encoded 32 bytes
  const b64 = Buffer.from(raw, 'base64');
  if (b64.length === 32) {
    return b64;
  }

  throw new Error(
    `CRITICAL: ZYPHON_MASTER_KEY must resolve to exactly 32 bytes (got ${buf.length} bytes)`,
  );
}

