/**
 * Encryption Module for Sensitive Data
 * Uses AES-256-GCM for encrypting configuration files
 */

import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';

const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32; // 256 bits
const IV_LENGTH = 16;  // 128 bits
const AUTH_TAG_LENGTH = 16;

/**
 * Get or create encryption key
 * Stored in a separate file with restrictive permissions
 */
function getEncryptionKey(): Buffer {
  const keyFile = path.join(os.homedir(), '.4runr', '.key');
  
  try {
    // Try to read existing key
    if (fs.existsSync(keyFile)) {
      const keyData = fs.readFileSync(keyFile);
      if (keyData.length === KEY_LENGTH) {
        return keyData;
      }
    }
    
    // Generate new key
    const key = crypto.randomBytes(KEY_LENGTH);
    
    // Ensure directory exists
    const dir = path.dirname(keyFile);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
    }
    
    // Save key with restrictive permissions
    fs.writeFileSync(keyFile, key, { mode: 0o600 });
    
    return key;
  } catch (error: any) {
    throw new Error(`Failed to manage encryption key: ${error.message}`);
  }
}

/**
 * Encrypt data
 */
export function encrypt(plaintext: string): string {
  try {
    const key = getEncryptionKey();
    const iv = crypto.randomBytes(IV_LENGTH);
    
    const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
    
    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    // Combine: iv + authTag + encrypted
    // Format: [iv:16][authTag:16][encrypted:variable]
    const combined = Buffer.concat([iv, authTag, Buffer.from(encrypted, 'hex')]);
    
    return combined.toString('base64');
  } catch (error: any) {
    throw new Error(`Encryption failed: ${error.message}`);
  }
}

/**
 * Decrypt data
 */
export function decrypt(ciphertext: string): string {
  try {
    const key = getEncryptionKey();
    const combined = Buffer.from(ciphertext, 'base64');
    
    // Extract: iv + authTag + encrypted
    const iv = combined.subarray(0, IV_LENGTH);
    const authTag = combined.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
    const encrypted = combined.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
    
    const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
    decipher.setAuthTag(authTag);
    
    let decrypted = decipher.update(encrypted, undefined, 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  } catch (error: any) {
    throw new Error(`Decryption failed: ${error.message}`);
  }
}

/**
 * Check if data is encrypted (base64 format)
 */
export function isEncrypted(data: string): boolean {
  try {
    // Try to decode as base64
    const decoded = Buffer.from(data, 'base64');
    // Must be at least iv + authTag + some data
    return decoded.length >= (IV_LENGTH + AUTH_TAG_LENGTH + 1) && 
           decoded.toString('base64') === data;
  } catch {
    return false;
  }
}

/**
 * Encrypt a JSON object
 */
export function encryptJson(obj: any): string {
  const json = JSON.stringify(obj);
  return encrypt(json);
}

/**
 * Decrypt to JSON object
 */
export function decryptJson(ciphertext: string): any {
  const json = decrypt(ciphertext);
  return JSON.parse(json);
}
