package com.arms.api.util.aes;

import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import jakarta.annotation.PostConstruct;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Arrays;

@Component
public class AES256 {

    private final AESProperty aesProperty;
    private String key;
    private static final int IV_SIZE = 12;
    private static final int TAG_LENGTH_BIT = 128;
    private static final String TRANSFORMATION = "AES/GCM/NoPadding";

    private AES256(AESProperty aesProperty) { this.aesProperty = aesProperty;}

    @PostConstruct
    public void init() {
        this.key = aesProperty.getToken(); // application.yml 에서 가져온 키 설정
    }

    private Key getAESKey() {
        byte[] keyBytes = new byte[32];
        byte[] b = key.getBytes(StandardCharsets.UTF_8);
        System.arraycopy(b, 0, keyBytes, 0, Math.min(b.length, keyBytes.length));
        return new SecretKeySpec(keyBytes, "AES");
    }

    private byte[] generateRandomIV() {
        byte[] iv = new byte[IV_SIZE];
        new SecureRandom().nextBytes(iv);
        return iv;
    }

    /*
     * AES-256 암호화
     */
    public String encrypt(String plainText) {
        try {
            if (ObjectUtils.isEmpty(this.key)) {
                return plainText;
            }

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            byte[] iv = generateRandomIV();

            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_BIT, iv);
            cipher.init(Cipher.ENCRYPT_MODE, getAESKey(), gcmParameterSpec);

            byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

            // IV + 암호문 결합 (IV를 함께 저장해야 복호화 가능)
            byte[] combined = new byte[IV_SIZE + encrypted.length];
            System.arraycopy(iv, 0, combined, 0, IV_SIZE);
            System.arraycopy(encrypted, 0, combined, IV_SIZE, encrypted.length);

            return Base64.encodeBase64String(combined);
        } catch (Exception e) {
            throw new RuntimeException("[AES256 :: encrypt] :: 암호화 중 오류 발생", e);
        }
    }

    /*
     * AES-256 복호화
     */
    public String decrypt(String encryptedText) {
        try {
            if (ObjectUtils.isEmpty(this.key)) {
                return encryptedText;
            }

            byte[] decodedBytes = Base64.decodeBase64(encryptedText);

            // IV 분리
            byte[] iv = Arrays.copyOfRange(decodedBytes, 0, IV_SIZE);
            byte[] cipherText = Arrays.copyOfRange(decodedBytes, IV_SIZE, decodedBytes.length);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH_BIT, iv);
            cipher.init(Cipher.DECRYPT_MODE, getAESKey(), gcmParameterSpec);

            return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);

        } catch (Exception e) {
            throw new RuntimeException("[AES256 :: decrypt] :: 복호화 중 오류 발생", e);
        }
    }
}
