package com.erp.ai.util;

import com.erp.ai.exception.AiAgentException;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
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.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * LLM API 키 AES-256-GCM 암복호화 유틸.
 * architecture §5.3 — base64( IV(12B) || CIPHERTEXT || TAG(16B) )
 *
 * 키는 application.yml ai.encryption-key (Base64 32B)에서 주입.
 */
@Slf4j
@Component
public class AesGcmCipher {

    private static final int IV_LENGTH = 12;
    private static final int TAG_LENGTH_BITS = 128;

    private final String encodedKey;
    private SecretKey secretKey;
    private final SecureRandom secureRandom = new SecureRandom();

    public AesGcmCipher(@Value("${ai.encryption-key}") String encodedKey) {
        this.encodedKey = encodedKey;
    }

    @PostConstruct
    public void init() {
        try {
            byte[] keyBytes = Base64.getDecoder().decode(encodedKey);
            if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) {
                // 잘못된 길이인 경우 SHA-256 으로 32 byte derived 키 사용
                java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256");
                keyBytes = md.digest(encodedKey.getBytes(StandardCharsets.UTF_8));
            }
            this.secretKey = new SecretKeySpec(keyBytes, "AES");
        } catch (Exception e) {
            log.error("AES-GCM 키 초기화 실패: {}", e.getMessage());
            throw new AiAgentException("암호화 키 초기화 실패", e);
        }
    }

    public String encrypt(String plain) {
        if (plain == null) return null;
        try {
            byte[] iv = new byte[IV_LENGTH];
            secureRandom.nextBytes(iv);

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BITS, iv));
            byte[] cipherText = cipher.doFinal(plain.getBytes(StandardCharsets.UTF_8));

            ByteBuffer buf = ByteBuffer.allocate(IV_LENGTH + cipherText.length);
            buf.put(iv).put(cipherText);
            return Base64.getEncoder().encodeToString(buf.array());
        } catch (Exception e) {
            throw new AiAgentException("암호화 실패: " + e.getMessage(), e);
        }
    }

    public String decrypt(String cipherTextB64) {
        if (cipherTextB64 == null) return null;
        try {
            byte[] all = Base64.getDecoder().decode(cipherTextB64);
            if (all.length < IV_LENGTH + 16) {
                throw new AiAgentException("암호문 길이가 잘못되었습니다");
            }
            byte[] iv = new byte[IV_LENGTH];
            byte[] ct = new byte[all.length - IV_LENGTH];
            System.arraycopy(all, 0, iv, 0, IV_LENGTH);
            System.arraycopy(all, IV_LENGTH, ct, 0, ct.length);

            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_LENGTH_BITS, iv));
            byte[] plain = cipher.doFinal(ct);
            return new String(plain, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new AiAgentException("복호화 실패: " + e.getMessage(), e);
        }
    }
}
