// 비밀번호 암호화/복호화 유틸리티
// 작성일: 2024-12-17

import crypto from "crypto";

export class PasswordEncryption {
  private static readonly ALGORITHM = "aes-256-cbc";
  private static readonly SECRET_KEY =
    process.env.DB_PASSWORD_SECRET ||
    "default-fallback-key-change-in-production";
  private static readonly IV_LENGTH = 16; // AES-CBC의 경우 16바이트

  /**
   * 비밀번호를 암호화합니다.
   * @param password 암호화할 평문 비밀번호
   * @returns 암호화된 비밀번호 (base64 인코딩)
   */
  static encrypt(password: string): string {
    try {
      // 랜덤 IV 생성
      const iv = crypto.randomBytes(this.IV_LENGTH);

      // 암호화 키 생성 (SECRET_KEY를 해시하여 32바이트 키 생성)
      const key = crypto.scryptSync(this.SECRET_KEY, "salt", 32);

      // 암호화 객체 생성 (IV를 명시적으로 사용)
      const cipher = crypto.createCipheriv(this.ALGORITHM, key, iv);

      // 암호화 실행
      let encrypted = cipher.update(password, "utf8", "hex");
      encrypted += cipher.final("hex");

      // IV와 암호화된 데이터를 결합하여 반환
      return `${iv.toString("hex")}:${encrypted}`;
    } catch (error) {
      console.error("Password encryption failed:", error);
      throw new Error("비밀번호 암호화에 실패했습니다.");
    }
  }

  /**
   * 암호화된 비밀번호를 복호화합니다.
   * @param encryptedPassword 암호화된 비밀번호
   * @returns 복호화된 평문 비밀번호
   */
  static decrypt(encryptedPassword: string): string {
    try {
      // IV와 암호화된 데이터 분리
      const parts = encryptedPassword.split(":");
      if (parts.length !== 2) {
        throw new Error("잘못된 암호화된 비밀번호 형식입니다.");
      }

      const iv = Buffer.from(parts[0], "hex");
      const encrypted = parts[1];

      // 암호화 키 생성 (암호화 시와 동일)
      const key = crypto.scryptSync(this.SECRET_KEY, "salt", 32);

      try {
        // 새로운 방식: createDecipheriv 사용 (IV 명시적 사용)
        const decipher = crypto.createDecipheriv(this.ALGORITHM, key, iv);
        let decrypted = decipher.update(encrypted, "hex", "utf8");
        decrypted += decipher.final("utf8");
        return decrypted;
      } catch (newFormatError: unknown) {
        const errorMessage =
          newFormatError instanceof Error
            ? newFormatError.message
            : String(newFormatError);
        console.warn(
          "새로운 복호화 방식 실패, 기존 방식으로 시도:",
          errorMessage
        );

        try {
          // 기존 방식: createDecipher 사용 (하위 호환성)
          const decipher = crypto.createDecipher("aes-256-cbc", key);
          let decrypted = decipher.update(encrypted, "hex", "utf8");
          decrypted += decipher.final("utf8");
          return decrypted;
        } catch (oldFormatError: unknown) {
          const oldErrorMessage =
            oldFormatError instanceof Error
              ? oldFormatError.message
              : String(oldFormatError);
          console.error("기존 복호화 방식도 실패:", oldErrorMessage);
          throw oldFormatError;
        }
      }
    } catch (error) {
      console.error("Password decryption failed:", error);
      throw new Error("비밀번호 복호화에 실패했습니다.");
    }
  }

  /**
   * 암호화 키가 설정되어 있는지 확인합니다.
   * @returns 키 설정 여부
   */
  static isKeyConfigured(): boolean {
    return (
      process.env.DB_PASSWORD_SECRET !== undefined &&
      process.env.DB_PASSWORD_SECRET !== ""
    );
  }

  /**
   * 암호화/복호화 기능을 테스트합니다.
   * @returns 테스트 결과
   */
  static testEncryption(): { success: boolean; message: string } {
    try {
      const testPassword = "test123!@#";
      const encrypted = this.encrypt(testPassword);
      const decrypted = this.decrypt(encrypted);

      if (testPassword === decrypted) {
        return {
          success: true,
          message: "암호화/복호화 테스트가 성공했습니다.",
        };
      } else {
        return {
          success: false,
          message: "암호화/복호화 결과가 일치하지 않습니다.",
        };
      }
    } catch (error) {
      return {
        success: false,
        message: `암호화/복호화 테스트 실패: ${error}`,
      };
    }
  }
}
