package de.assecutor.votianlt.service;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;

/**
 * Symmetrische AES-256-GCM-Verschlüsselung mit Master-Key-Ableitung über SHA-256.
 *
 * Format der erzeugten Bytes: {@code IV (12 Byte) || Ciphertext+Tag (16 Byte Tag)}.
 * Der IV wird pro Verschlüsselung neu zufällig erzeugt; ein Master-Key liefert
 * deterministisch denselben AES-Schlüssel.
 *
 * Nicht für hochfrequente Krypto-Operationen optimiert — bewusst minimal gehalten.
 */
final class AesGcmCipher {

    private static final String TRANSFORMATION = "AES/GCM/NoPadding";
    private static final int IV_LENGTH = 12;
    private static final int TAG_LENGTH_BITS = 128;

    private final SecretKeySpec key;
    private final SecureRandom random = new SecureRandom();

    AesGcmCipher(String masterKey) {
        if (masterKey == null || masterKey.length() < 16) {
            throw new IllegalArgumentException("Master-Key muss mindestens 16 Zeichen lang sein.");
        }
        try {
            byte[] derived = MessageDigest.getInstance("SHA-256").digest(masterKey.getBytes("UTF-8"));
            this.key = new SecretKeySpec(derived, "AES");
        } catch (Exception ex) {
            throw new IllegalStateException("Master-Key konnte nicht abgeleitet werden.", ex);
        }
    }

    byte[] encrypt(byte[] plaintext) {
        try {
            byte[] iv = new byte[IV_LENGTH];
            random.nextBytes(iv);
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, iv));
            byte[] ciphertext = cipher.doFinal(plaintext);
            return ByteBuffer.allocate(IV_LENGTH + ciphertext.length).put(iv).put(ciphertext).array();
        } catch (Exception ex) {
            throw new IllegalStateException("Verschlüsselung fehlgeschlagen: " + ex.getMessage(), ex);
        }
    }

    byte[] decrypt(byte[] ivAndCiphertext) {
        if (ivAndCiphertext == null || ivAndCiphertext.length < IV_LENGTH + 16) {
            throw new IllegalArgumentException("Ciphertext zu kurz.");
        }
        try {
            ByteBuffer buffer = ByteBuffer.wrap(ivAndCiphertext);
            byte[] iv = new byte[IV_LENGTH];
            buffer.get(iv);
            byte[] ciphertext = new byte[buffer.remaining()];
            buffer.get(ciphertext);

            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(TAG_LENGTH_BITS, iv));
            return cipher.doFinal(ciphertext);
        } catch (Exception ex) {
            throw new IllegalStateException("Entschlüsselung fehlgeschlagen: " + ex.getMessage(), ex);
        }
    }
}
