package it.enea.cruise.service.idp;


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

import lombok.extern.log4j.Log4j2;

import javax.annotation.PostConstruct;
import javax.crypto.*;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

//@Service
@Log4j2
public class RSAEncryptionManager implements IEncryptionManager {
	
	@Value("${testingPhase}")
	private Boolean testingPhase;
	
	@Value("${idp.keyManager.privateKeyContent}")
	private String privateKeyContent;
	
	@Value("${idp.keyManager.publicKeyContent}")
	private String publicKeyContent;

	
	private final byte[] salt = { (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03, (byte) 0xA9, (byte) 0x9B,
			(byte) 0xC8, (byte) 0x32 };
	private final int iterationCount = 19;

	private SecretKey key;
	private Cipher encryptCipher;
	private Cipher decryptCipher;
	
	public RSAEncryptionManager() {
			
	}
	
	@PostConstruct
    public void init() {
	
		privateKeyContent = privateKeyContent.replaceAll("\n", "").replace("-----BEGIN PRIVATE KEY-----", "")
				.replace("-----END PRIVATE KEY-----", "");

		publicKeyContent = publicKeyContent.replaceAll("\n", "").replace("-----BEGIN PUBLIC KEY-----", "")
				.replace("-----END PUBLIC KEY-----", "");

		KeyFactory kf;
		try {
			kf = KeyFactory.getInstance("RSA");
			PKCS8EncodedKeySpec keySpecPKCS8 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyContent));

			PrivateKey privKey = kf.generatePrivate(keySpecPKCS8);

			X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyContent));

			RSAPublicKey pubKey = (RSAPublicKey) kf.generatePublic(keySpecX509);

			encryptCipher = Cipher.getInstance("RSA");
			encryptCipher.init(Cipher.ENCRYPT_MODE, privKey);

			decryptCipher = Cipher.getInstance("RSA");
			decryptCipher.init(Cipher.DECRYPT_MODE, pubKey);

		} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException e) {
			// TODO Auto-generated catch block
			log.error("Unable to initialize encryption components", e);
			throw new RuntimeException("Unable to initialize encryption components", e);
		}

	}

	public void init(String password) {
		try {
			KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iterationCount);
			key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

			encryptCipher = Cipher.getInstance(key.getAlgorithm());
			decryptCipher = Cipher.getInstance(key.getAlgorithm());

			AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);
			encryptCipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
			decryptCipher.init(Cipher.DECRYPT_MODE, key, paramSpec);
		} catch (Exception e) {
			log.error("Unable to initialize encryption components", e);
			throw new RuntimeException("Unable to initialize encryption components", e);
		}
	}

	public String encrypt(String s) {
		try {
			byte[] enc = encryptCipher.doFinal(s.getBytes(StandardCharsets.UTF_8));
			return (new String(Base64.getEncoder().encode(enc)));
		} catch (Exception e) {
			log.error("Unable to encrypt message", e);
		}

		return null;
	}

	public String decrypt(String s) {
		try {
			byte[] dec = Base64.getDecoder().decode(s.getBytes(StandardCharsets.UTF_8));
			byte[] res = decryptCipher.doFinal(dec);
			return (new String(res, StandardCharsets.UTF_8));
		} catch (Exception e) {
			log.error("Unable to decrypt message", e);
		}

		return null;
	}
}