/**
 * Log Encryption at Rest
 * Encrypts log files before writing to disk
 */

import * as crypto from 'crypto';
import * as fs from 'fs/promises';
import * as path from 'path';
import { createLogger } from '@4runr/shared';

const logger = createLogger('LogEncryption');

interface EncryptionConfig {
  enabled: boolean;
  key?: Buffer;
  algorithm: string;
}

let config: EncryptionConfig = {
  enabled: false,
  algorithm: 'aes-256-gcm'
};

/**
 * Initialize log encryption
 */
export function initLogEncryption(encryptionKey?: string): void {
  if (!encryptionKey) {
    config.enabled = false;
    logger.warn('Log encryption disabled - no encryption key provided');
    return;
  }

  try {
    // Derive key from provided key (use HKDF for key derivation)
    const keyMaterial = Buffer.from(encryptionKey, 'utf-8');
    config.key = crypto.createHash('sha256').update(keyMaterial).digest();
    config.enabled = true;
    logger.info('Log encryption enabled');
  } catch (error) {
    logger.error('Failed to initialize log encryption', {
      error: error instanceof Error ? error.message : String(error)
    });
    config.enabled = false;
  }
}

/**
 * Encrypt log entry before writing
 */
function encryptLogEntry(plaintext: string): string {
  if (!config.enabled || !config.key) {
    return plaintext; // Return plaintext if encryption disabled
  }

  try {
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv(config.algorithm, config.key, iv);
    
    let encrypted = cipher.update(plaintext, 'utf-8', 'hex');
    encrypted += cipher.final('hex');
    
    // Get auth tag for GCM
    const authTag = (cipher as any).getAuthTag();
    
    // Format: iv:authTag:encrypted
    return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
  } catch (error) {
    logger.error('Failed to encrypt log entry', {
      error: error instanceof Error ? error.message : String(error)
    });
    // Fallback to plaintext on encryption error
    return plaintext;
  }
}

/**
 * Decrypt log entry for reading
 */
export function decryptLogEntry(encrypted: string): string {
  if (!config.enabled || !config.key) {
    return encrypted; // Return as-is if encryption disabled
  }

  try {
    // Check if it's encrypted (has format iv:authTag:encrypted)
    const parts = encrypted.split(':');
    if (parts.length !== 3) {
      // Not encrypted, return as-is
      return encrypted;
    }

    const [ivHex, authTagHex, encryptedHex] = parts;
    
    // Validate all parts exist
    if (!ivHex || !authTagHex || !encryptedHex) {
      return encrypted; // Invalid format, return as-is
    }
    
    const iv = Buffer.from(ivHex, 'hex');
    const authTag = Buffer.from(authTagHex, 'hex');
    const encryptedData = Buffer.from(encryptedHex, 'hex');

    const decipher = crypto.createDecipheriv(config.algorithm, config.key!, iv);
    (decipher as any).setAuthTag(authTag);
    
    let decrypted = decipher.update(encryptedData, undefined, 'utf-8');
    decrypted += decipher.final('utf-8');
    
    return decrypted;
  } catch (error) {
    logger.error('Failed to decrypt log entry', {
      error: error instanceof Error ? error.message : String(error)
    });
    // Return encrypted string if decryption fails
    return encrypted;
  }
}

/**
 * Write encrypted log to file
 */
export async function writeEncryptedLog(
  filePath: string,
  logEntry: string
): Promise<void> {
  try {
    const encrypted = encryptLogEntry(logEntry);
    
    // Ensure directory exists
    const dir = path.dirname(filePath);
    await fs.mkdir(dir, { recursive: true });
    
    // Append encrypted log entry
    await fs.appendFile(filePath, encrypted + '\n', 'utf-8');
  } catch (error) {
    logger.error('Failed to write encrypted log', {
      filePath,
      error: error instanceof Error ? error.message : String(error)
    });
    throw error;
  }
}

/**
 * Read and decrypt log file
 */
export async function readEncryptedLog(filePath: string): Promise<string[]> {
  try {
    const content = await fs.readFile(filePath, 'utf-8');
    const lines = content.split('\n').filter(line => line.trim());
    
    return lines.map(line => decryptLogEntry(line));
  } catch (error) {
    logger.error('Failed to read encrypted log', {
      filePath,
      error: error instanceof Error ? error.message : String(error)
    });
    throw error;
  }
}

/**
 * Create encrypted log stream
 */
export function createEncryptedLogStream(filePath: string): {
  write: (entry: string) => Promise<void>;
  close: () => Promise<void>;
} {
  let buffer: string[] = [];
  let writePromise: Promise<void> | null = null;

  const flush = async () => {
    if (buffer.length === 0) return;
    
    const toWrite = buffer.join('\n') + '\n';
    buffer = [];
    
    const encrypted = encryptLogEntry(toWrite);
    await fs.appendFile(filePath, encrypted, 'utf-8');
  };

  return {
    write: async (entry: string) => {
      buffer.push(entry);
      
      // Flush every 10 entries or immediately if buffer is getting large
      if (buffer.length >= 10) {
        if (writePromise) {
          await writePromise;
        }
        writePromise = flush();
        await writePromise;
        writePromise = null;
      }
    },
    close: async () => {
      if (writePromise) {
        await writePromise;
      }
      await flush();
    }
  };
}

/**
 * Check if log encryption is enabled
 */
export function isLogEncryptionEnabled(): boolean {
  return config.enabled;
}

