/**
 * Crypto Service
 *
 * Handles encryption/decryption for sensitive data.
 */

import { Injectable, Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { createCipheriv, createDecipheriv, randomBytes } from "crypto";

@Injectable()
export class CryptoService {
  private readonly logger = new Logger(CryptoService.name);
  private readonly algorithm = "aes-256-gcm";
  private readonly encryptionKey: Buffer;

  constructor(private readonly config: ConfigService) {
    const keyHex = this.config.get<string>("ENCRYPTION_KEY");
    if (!keyHex) {
      throw new Error("ENCRYPTION_KEY environment variable is required for private key encryption");
    }

    // Validate key is 64 hex characters (32 bytes for AES-256)
    if (!/^[0-9a-fA-F]{64}$/.test(keyHex)) {
      throw new Error("ENCRYPTION_KEY must be 64 hexadecimal characters (32 bytes)");
    }

    this.encryptionKey = Buffer.from(keyHex, "hex");

    // Validate key works by performing encrypt/decrypt round-trip
    // This prevents silent data loss if the key is changed after data is encrypted
    try {
      const testValue = "encryption_key_validation_test";
      const encrypted = this.encrypt(testValue);
      const decrypted = this.decrypt(encrypted);

      if (decrypted !== testValue) {
        throw new Error("Encryption key validation failed: round-trip mismatch");
      }
    } catch (error) {
      const errorMsg =
        error instanceof Error ? error.message : "Unknown encryption key validation error";
      throw new Error(
        `ENCRYPTION_KEY validation failed: ${errorMsg}. ` +
          "If you recently changed the key, existing encrypted data cannot be decrypted. " +
          "See docs/design/credential-security.md for key rotation procedures."
      );
    }

    this.logger.log("Crypto service initialized with AES-256-GCM encryption");
  }

  /**
   * Encrypt sensitive data (e.g., private keys)
   * Returns base64-encoded string with format: iv:authTag:encrypted
   */
  encrypt(plaintext: string): string {
    try {
      // Generate random IV (12 bytes for GCM)
      const iv = randomBytes(12);

      // Create cipher
      const cipher = createCipheriv(this.algorithm, this.encryptionKey, iv);

      // Encrypt data
      let encrypted = cipher.update(plaintext, "utf8", "hex");
      encrypted += cipher.final("hex");

      // Get auth tag
      const authTag = cipher.getAuthTag();

      // Return as iv:authTag:encrypted (all hex-encoded)
      return `${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted}`;
    } catch (error) {
      this.logger.error("Encryption failed", error);
      throw new Error("Failed to encrypt data");
    }
  }

  /**
   * Decrypt sensitive data
   * Expects format: iv:authTag:encrypted (all hex-encoded)
   */
  decrypt(encrypted: string): string {
    try {
      // Parse encrypted data
      const parts = encrypted.split(":");
      if (parts.length !== 3) {
        throw new Error("Invalid encrypted data format");
      }

      const ivHex = parts[0];
      const authTagHex = parts[1];
      const encryptedData = parts[2];

      if (!ivHex || !authTagHex || !encryptedData) {
        throw new Error("Invalid encrypted data format");
      }

      const iv = Buffer.from(ivHex, "hex");
      const authTag = Buffer.from(authTagHex, "hex");

      // Create decipher
      const decipher = createDecipheriv(this.algorithm, this.encryptionKey, iv);
      decipher.setAuthTag(authTag);

      // Decrypt data
      const decryptedBuffer = decipher.update(encryptedData, "hex");
      const finalBuffer = decipher.final();
      const decrypted = Buffer.concat([decryptedBuffer, finalBuffer]).toString("utf8");

      return decrypted;
    } catch (error) {
      // Security: Do not log error details which may contain sensitive data
      // Only log error type/code without stack trace or encrypted content
      const errorType = error instanceof Error ? error.constructor.name : "Unknown";
      this.logger.error(`Decryption failed: ${errorType}`);
      throw new Error("Failed to decrypt data");
    }
  }
}
