import crypto from "crypto";

/**
 * 기존 Java EncryptUtil과 동일한 AES 암호화 로직
 * AES/ECB/NoPadding 방식 사용
 */
export class EncryptUtil {
  private static readonly keyName = "ILJIAESSECRETKEY"; // 16자리 키
  private static readonly algorithm = "AES";

  /**
   * 문자열을 AES로 암호화
   * @param source 원본 문자열
   * @returns 암호화된 16진수 문자열
   */
  public static encrypt(source: string): string {
    try {
      const key = Buffer.from(this.keyName, "utf8");
      const paddedSource = this.addPadding(Buffer.from(source, "utf8"));

      const cipher = crypto.createCipher("aes-128-ecb", key);
      cipher.setAutoPadding(false); // NoPadding 모드

      const encrypted = Buffer.concat([
        cipher.update(paddedSource),
        cipher.final(),
      ]);

      return this.fromHex(encrypted);
    } catch (error) {
      console.error("암호화 실패:", error);
      throw new Error("암호화 중 오류가 발생했습니다.");
    }
  }

  /**
   * 암호화된 문자열을 복호화
   * @param source 암호화된 16진수 문자열
   * @returns 복호화된 원본 문자열
   */
  public static decrypt(source: string): string {
    try {
      const key = Buffer.from(this.keyName, "utf8");
      const encryptedBytes = this.toBytes(source);

      const decipher = crypto.createDecipher("aes-128-ecb", key);
      decipher.setAutoPadding(false); // NoPadding 모드

      const decrypted = Buffer.concat([
        decipher.update(encryptedBytes),
        decipher.final(),
      ]);

      const unpadded = this.removePadding(decrypted);
      return unpadded.toString("utf8");
    } catch (error) {
      console.error("복호화 실패:", error);
      throw new Error("복호화 중 오류가 발생했습니다.");
    }
  }

  /**
   * SHA-256 해시 생성
   * @param source 원본 문자열
   * @returns SHA-256 해시 문자열
   */
  public static encryptSha256(source: string): string {
    try {
      const hash = crypto.createHash("sha256");
      hash.update(source);
      return hash.digest("hex");
    } catch (error) {
      console.error("SHA-256 해시 생성 실패:", error);
      throw new Error("해시 생성 중 오류가 발생했습니다.");
    }
  }

  /**
   * 패스워드 검증 (암호화된 패스워드와 평문 패스워드 비교)
   * @param plainPassword 평문 패스워드
   * @param encryptedPassword 암호화된 패스워드
   * @returns 일치 여부
   */
  public static matches(
    plainPassword: string,
    encryptedPassword: string
  ): boolean {
    try {
      const encrypted = this.encrypt(plainPassword);
      return encrypted === encryptedPassword;
    } catch (error) {
      console.error("패스워드 검증 실패:", error);
      return false;
    }
  }

  /**
   * 바이트 배열을 16진수 문자열로 변환
   */
  private static fromHex(bytes: Buffer): string {
    return bytes.toString("hex");
  }

  /**
   * 16진수 문자열을 바이트 배열로 변환
   */
  private static toBytes(source: string): Buffer {
    const buffer = Buffer.alloc(source.length / 2);
    for (let i = 0; i < source.length; i += 2) {
      buffer[i / 2] = parseInt(source.substr(i, 2), 16);
    }
    return buffer;
  }

  /**
   * 패딩 추가 (16바이트 블록 크기에 맞춤)
   */
  private static addPadding(bytes: Buffer): Buffer {
    const blockSize = 16;
    const paddingSize = blockSize - (bytes.length % blockSize);
    const padded = Buffer.alloc(bytes.length + paddingSize);

    bytes.copy(padded);
    // 나머지 부분을 0x00으로 채움
    for (let i = bytes.length; i < padded.length; i++) {
      padded[i] = 0x00;
    }

    return padded;
  }

  /**
   * 패딩 제거
   */
  private static removePadding(bytes: Buffer): Buffer {
    let endIndex = bytes.length - 1;

    // 끝에서부터 0x00이 아닌 첫 번째 바이트를 찾음
    while (endIndex >= 0 && bytes[endIndex] === 0x00) {
      endIndex--;
    }

    return bytes.slice(0, endIndex + 1);
  }
}
