import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';

export interface EncryptionResult {
  encrypted: string;
  keyId: string;
}

@Injectable()
export class AesEncryptionService {
  private readonly logger = new Logger(AesEncryptionService.name);
  private readonly algorithm = 'aes-256-gcm';
  private readonly keyMap: Map<string, Buffer> = new Map();
  private readonly currentKeyId: string;

  constructor(private readonly configService: ConfigService) {
    // Load encryption keys
    const encryptionKey = this.configService.get<string>('BACKUP_ENCRYPTION_KEY');
    const keyId = this.configService.get<string>('BACKUP_ENCRYPTION_KEY_ID') || 'key-v1';

    if (!encryptionKey) {
      throw new Error('BACKUP_ENCRYPTION_KEY is not configured');
    }

    // Convert hex string to buffer
    const keyBuffer = Buffer.from(encryptionKey, 'hex');
    if (keyBuffer.length !== 32) {
      throw new Error('BACKUP_ENCRYPTION_KEY must be 256 bits (64 hex characters)');
    }

    this.keyMap.set(keyId, keyBuffer);
    this.currentKeyId = keyId;

    this.logger.log(`AES encryption service initialized with key: ${keyId}`);
  }

  async encrypt(plaintext: string): Promise<EncryptionResult> {
    const key = this.keyMap.get(this.currentKeyId);
    if (!key) {
      throw new Error(`Encryption key not found: ${this.currentKeyId}`);
    }

    // Generate random IV (12 bytes for GCM)
    const iv = crypto.randomBytes(12);

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

    // Encrypt
    let encrypted = cipher.update(plaintext, 'utf8', 'base64');
    encrypted += cipher.final('base64');

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

    // Combine: base64(ciphertext):base64(iv):base64(authTag)
    const combined = `${encrypted}:${iv.toString('base64')}:${authTag.toString('base64')}`;

    return {
      encrypted: combined,
      keyId: this.currentKeyId,
    };
  }

  async decrypt(encryptedData: string, keyId: string): Promise<string> {
    const key = this.keyMap.get(keyId);
    if (!key) {
      throw new Error(`Decryption key not found: ${keyId}`);
    }

    // Parse combined string
    const parts = encryptedData.split(':');
    if (parts.length !== 3) {
      throw new Error('Invalid encrypted data format');
    }

    const [ciphertext, ivBase64, authTagBase64] = parts;
    const iv = Buffer.from(ivBase64, 'base64');
    const authTag = Buffer.from(authTagBase64, 'base64');

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

    // Decrypt
    let decrypted = decipher.update(ciphertext, 'base64', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted;
  }

  // For key rotation - add a new key
  addKey(keyId: string, keyHex: string): void {
    const keyBuffer = Buffer.from(keyHex, 'hex');
    if (keyBuffer.length !== 32) {
      throw new Error('Key must be 256 bits (64 hex characters)');
    }
    this.keyMap.set(keyId, keyBuffer);
    this.logger.log(`Added encryption key: ${keyId}`);
  }

  getCurrentKeyId(): string {
    return this.currentKeyId;
  }
}
