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

const ALGORITHM = "aes-256-gcm";
const IV_LENGTH = 12; // 96-bit IV recomendado para GCM
const AUTH_TAG_LENGTH = 16;

export interface EncryptedPayload {
  iv: string; // base64
  authTag: string; // base64
  ciphertext: string; // base64
  v: 1;
}

@Injectable()
export class EncryptionService implements OnModuleInit {
  private masterKey!: Buffer;

  constructor(private readonly config: ConfigService) {}

  onModuleInit(): void {
    const raw = this.config.get<string>("MASTER_ENCRYPTION_KEY");
    if (!raw) {
      throw new Error("MASTER_ENCRYPTION_KEY no está configurado");
    }
    const key = Buffer.from(raw, "base64");
    if (key.length !== 32) {
      throw new Error(
        `MASTER_ENCRYPTION_KEY debe ser 32 bytes en base64 (recibido: ${key.length} bytes)`,
      );
    }
    this.masterKey = key;
  }

  encrypt(plaintext: string): EncryptedPayload {
    const iv = randomBytes(IV_LENGTH);
    const cipher = createCipheriv(ALGORITHM, this.masterKey, iv);
    const ciphertext = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
    const authTag = cipher.getAuthTag();
    return {
      iv: iv.toString("base64"),
      authTag: authTag.toString("base64"),
      ciphertext: ciphertext.toString("base64"),
      v: 1,
    };
  }

  decrypt(payload: EncryptedPayload): string {
    if (payload.v !== 1) {
      throw new Error(`Encryption payload version desconocida: ${payload.v}`);
    }
    const iv = Buffer.from(payload.iv, "base64");
    const authTag = Buffer.from(payload.authTag, "base64");
    const ciphertext = Buffer.from(payload.ciphertext, "base64");
    if (authTag.length !== AUTH_TAG_LENGTH) {
      throw new Error("authTag length inválido");
    }
    const decipher = createDecipheriv(ALGORITHM, this.masterKey, iv);
    decipher.setAuthTag(authTag);
    const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
    return plaintext.toString("utf8");
  }

  encryptJson<T>(value: T): EncryptedPayload {
    return this.encrypt(JSON.stringify(value));
  }

  decryptJson<T>(payload: EncryptedPayload): T {
    return JSON.parse(this.decrypt(payload)) as T;
  }
}
