/**
 * AES-256-GCM encryption/decryption for protecting cloud drive cookies stored in DB.
 *
 * Encryption key is derived from COOKIE_ENCRYPTION_KEY env var via SHA-256.
 * If unset, uses a built-in default key (stable across container restarts).
 * Production MUST set COOKIE_ENCRYPTION_KEY!
 */
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';

const ALGORITHM = 'aes-256-gcm';
const IV_LENGTH = 12;  // 96-bit nonce for GCM
const TAG_LENGTH = 16; // 128-bit auth tag
const KEY_LENGTH = 32; // 256-bit key

let ENCRYPTION_KEY: Buffer | null = null;

function getKey(): Buffer {
  if (ENCRYPTION_KEY) return ENCRYPTION_KEY;

  const envKey = process.env.COOKIE_ENCRYPTION_KEY;
  if (envKey && envKey.length >= 32) {
    ENCRYPTION_KEY = crypto.createHash('sha256').update(envKey).digest();
    console.log('[Crypto] Cookie encryption enabled (key from COOKIE_ENCRYPTION_KEY)');
  } else if (envKey) {
    ENCRYPTION_KEY = crypto.createHash('sha256').update(envKey).digest();
    console.log('[Crypto] Cookie encryption enabled (key from COOKIE_ENCRYPTION_KEY, SHA-256 derived)');
  } else {
    // Auto-generate a random key and persist to file
    const keyFile = path.join(process.env.DATA_DIR || '/app/data', 'encryption.key');
    try {
      if (fs.existsSync(keyFile)) {
        const savedKey = fs.readFileSync(keyFile, 'utf8').trim();
        if (savedKey.length >= 32) {
          ENCRYPTION_KEY = crypto.createHash('sha256').update(savedKey).digest();
          console.log('[Crypto] Cookie encryption enabled (loaded from encryption.key)');
          return ENCRYPTION_KEY;
        }
      }
    } catch (_) { /* file read failed, will generate new key */ }

    const autoKey = crypto.randomBytes(32).toString('hex');
    try {
      const dir = path.dirname(keyFile);
      if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
      fs.writeFileSync(keyFile, autoKey);
      console.log('[Crypto] Auto-generated encryption key saved to encryption.key');
    } catch (e: any) {
      console.log('[Crypto] Could not persist key, using ephemeral:', e.message);
    }
    ENCRYPTION_KEY = crypto.createHash('sha256').update(autoKey).digest();
    console.log('[Crypto] Cookie encryption enabled (auto-generated key)');
  }
  return ENCRYPTION_KEY;
}

/**
 * Encrypt plaintext. Returns base64-encoded ciphertext (includes IV + auth tag).
 */
export function encrypt(plaintext: string): string {
  if (!plaintext) return '';
  const key = getKey();
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
  const encrypted = Buffer.concat([
    cipher.update(plaintext, 'utf8'),
    cipher.final(),
  ]);
  const tag = cipher.getAuthTag();
  // Format: iv (12) + tag (16) + ciphertext
  const combined = Buffer.concat([iv, tag, encrypted]);
  return combined.toString('base64');
}

/**
 * Decrypt base64-encoded ciphertext. Returns original plaintext.
 * Returns empty string if decryption fails (corrupted data or wrong key).
 */
export function decrypt(encoded: string): string {
  if (!encoded) return '';
  try {
    const key = getKey();
    const combined = Buffer.from(encoded, 'base64');
    if (combined.length < IV_LENGTH + TAG_LENGTH + 1) {
      console.warn('[Crypto] Ciphertext too short, returning as-is (possibly unencrypted legacy data)');
      return encoded;
    }
    const iv = combined.subarray(0, IV_LENGTH);
    const tag = combined.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
    const ciphertext = combined.subarray(IV_LENGTH + TAG_LENGTH);
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
    decipher.setAuthTag(tag);
    const decrypted = Buffer.concat([
      decipher.update(ciphertext),
      decipher.final(),
    ]);
    return decrypted.toString('utf8');
  } catch (err: any) {
    if (err.message?.includes('unsupported state') || err.message?.includes('authentication')) {
      console.warn('[Crypto] Decryption failed (possibly legacy plaintext), returning as-is');
      return encoded;
    }
    console.error('[Crypto] Decryption error:', err.message);
    return '';
  }
}

/**
 * Check if a string appears to be encrypted (base64 with IV+tag prefix).
 * Used for migration: re-encrypt legacy plaintext cookies.
 */
export function isEncrypted(value: string): boolean {
  if (!value) return false;
  try {
    const combined = Buffer.from(value, 'base64');
    return combined.length > IV_LENGTH + TAG_LENGTH;
  } catch {
    return false;
  }
}
