package com.owrawww.util;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.HexFormat;

/**
 * AES-256-GCM 암호화/복호화 유틸리티
 * - IV(12 bytes) + 암호문을 Base64로 인코딩하여 저장
 */
@Component
public class AesUtil {

    private static final String ALGORITHM   = "AES/GCM/NoPadding";
    private static final int    IV_LENGTH   = 12;   // GCM 권장 IV 크기
    private static final int    TAG_BIT_LEN = 128;  // GCM 인증 태그 크기

    private final SecretKey secretKey;
    private final String    hashSalt;

    public AesUtil(
            @Value("${app.encryption.key}") String base64Key,
            @Value("${app.hash.salt}") String hashSalt) {
        byte[] keyBytes = Base64.getDecoder().decode(base64Key);
        if (keyBytes.length != 32) {
            throw new IllegalArgumentException("암호화 키는 Base64 인코딩된 32바이트(256bit)여야 합니다.");
        }
        this.secretKey = new SecretKeySpec(keyBytes, "AES");
        this.hashSalt  = hashSalt;
    }

    /**
     * 평문 → AES-256-GCM 암호화 → Base64 문자열
     */
    public String encrypt(String plainText) {
        if (plainText == null || plainText.isEmpty()) return plainText;
        try {
            byte[] iv = new byte[IV_LENGTH];
            new SecureRandom().nextBytes(iv);

            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_BIT_LEN, iv));
            byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));

            // IV + 암호문 결합 후 Base64 인코딩
            byte[] combined = new byte[IV_LENGTH + encrypted.length];
            System.arraycopy(iv, 0, combined, 0, IV_LENGTH);
            System.arraycopy(encrypted, 0, combined, IV_LENGTH, encrypted.length);

            return Base64.getEncoder().encodeToString(combined);
        } catch (Exception e) {
            throw new RuntimeException("암호화 실패", e);
        }
    }

    /**
     * Base64 암호문 → AES-256-GCM 복호화 → 평문
     */
    public String decrypt(String encryptedText) {
        if (encryptedText == null || encryptedText.isEmpty()) return encryptedText;
        try {
            byte[] combined = Base64.getDecoder().decode(encryptedText);
            byte[] iv        = new byte[IV_LENGTH];
            byte[] cipherText = new byte[combined.length - IV_LENGTH];

            System.arraycopy(combined, 0, iv, 0, IV_LENGTH);
            System.arraycopy(combined, IV_LENGTH, cipherText, 0, cipherText.length);

            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_BIT_LEN, iv));
            return new String(cipher.doFinal(cipherText), "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException("복호화 실패", e);
        }
    }

    /**
     * 평문 → SHA-256(솔트 + 평문) → HEX 문자열 (검색/중복체크용)
     * - 단방향: 복호화 불가
     * - 솔트로 레인보우 테이블 공격 방어
     */
    public String hash(String plainText) {
        if (plainText == null || plainText.isEmpty()) return plainText;
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(
                (hashSalt + plainText).getBytes(StandardCharsets.UTF_8)
            );
            return HexFormat.of().formatHex(hashBytes);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 알고리즘을 사용할 수 없습니다.", e);
        }
    }
}
