package com.bb.util;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AesUtil {
	
	private static final String ALGO = "AES/GCM/NoPadding";
    private static final int IV_LENGTH = 12;      // GCM 표준
    private static final int TAG_LENGTH = 128;    // 인증 태그
    
	// ==== [AES 암호화(인코딩) 메소드] ====
	public static String getAES128_Encode(String data) {
		try {
			String secretKey = "9944184563minepw"; //비밀키 선언 16바이트
    		
			byte ivBytes[] = new byte[16]; //AES128비트 암호화에서 16바이트는 변할 수 없다 
			Arrays.fill(ivBytes, (byte)0x00); //배열에 초기값 0으로 삽입 실시
        	
			byte textBytes[] = data.getBytes("UTF-8");
        	
			AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
			SecretKeySpec newKey = new SecretKeySpec(secretKey.getBytes("UTF-8"),"AES");
			Cipher cipher = null;
			cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec);
        	        	
			Encoder encoder = Base64.getEncoder(); //base64로 다시 포맷해서 인코딩 실시 (경우에 따라 아파치 base64 필요)
			return encoder.encodeToString(cipher.doFinal(textBytes));        	
		}
		catch(Exception e) {
			System.out.println(e.getMessage());    	
		}
		return "";
	}
	
	// ==== [AES 복호화(디코딩) 메소드] ====
	public static String getAES128_Decode(String data) {
		try {
			String secretKey = "9944184563minepw"; //비밀키 선언 16바이트
    		
			byte ivBytes[] = new byte[16]; //AES128비트 암호화에서 16바이트는 변할 수 없다 
			Arrays.fill(ivBytes, (byte)0x00); //배열에 초기값 0으로 삽입 실시
        	
			Decoder decoder = Base64.getDecoder(); //base64로 다시 포맷해서 디코딩 실시 (경우에 따라 아파치 base64 필요)
			byte textBytes[] = decoder.decode(data);
    		
			AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivBytes);
			SecretKeySpec newKey = new SecretKeySpec(secretKey.getBytes("UTF-8"),"AES");
			Cipher cipher = null;
			cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			cipher.init(Cipher.DECRYPT_MODE, newKey, ivSpec);    
        	
			return new String(cipher.doFinal(textBytes),"UTF-8");
		}
		catch (Exception e) {
			System.out.println(e.getMessage());
		}
		return "";
	}
	
	public static String encrypt(String plainText) throws Exception {
	    String secretKey = "gwTb1FnH9RbxORzo"; // 반드시 16/24/32 byte
        byte[] iv = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(iv);

        Cipher cipher = Cipher.getInstance(ALGO);
        SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv);

        cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec);

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

        // iv + ciphertext 합쳐서 URL-safe 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.getUrlEncoder().withoutPadding().encodeToString(combined);
    }
	
	public static String decrypt(String encoded) throws Exception {
		String secretKey = "gwTb1FnH9RbxORzo"; // 반드시 16/24/32 byte
        byte[] combined = Base64.getUrlDecoder().decode(encoded);

        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(ALGO);
        SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH, iv);

        cipher.init(Cipher.DECRYPT_MODE, keySpec, spec);

        byte[] decrypted = cipher.doFinal(cipherText);

        return new String(decrypted, StandardCharsets.UTF_8);
    }

}
