/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.etee.crypto.examples;


import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Map;

import javax.crypto.SecretKey;

import be.fgov.ehealth.etee.crypto.encrypt.EncryptionToken;
import be.fgov.ehealth.etee.crypto.encrypt.EncryptionTokenFactory;
import be.fgov.ehealth.etee.crypto.utils.KeyManager;
import be.fgov.ehealth.etee.crypto.utils.SecretKeyUtils;


/**
 * Properties of Alice and Bob, often used in examples and tests, Alice as sender, Bob as receiver.
 * 
 * The properties are lazy initialized for speedy tests, and exception handling is also handled here.
 * 
 * @author alde
 * 
 */
public final class ExampleProperties {

    private static final ExampleProperties LOCAL_INSTANCE = new ExampleProperties();

    /** Alice's resources subfolder. */
    private static final String ALICE_FOLDER = "/alice";

    /** Bob's resources subfolder. */
    private static final String BOB_FOLDER = "/bob";

    public static final String ALICES_PUBLIC_ENCRYPTION_TOKEN_FILE = ALICE_FOLDER + "/alices_public_key.etk";

    public static final String ALICES_PRIVATE_KEYSTORE_FILE = ALICE_FOLDER + "/alices_private_key_store.p12";

    public static final String ALICES_PRIVATE_KEYSTORE_TYPE = "PKCS12";

    public static final char[] ALICES_PRIVATE_KEYSTORE_PASSWORD = "test".toCharArray();

    public static final String ALICES_AUTHENTICATION_KEY_ALIAS = "Authentication";

    public static final char[] ALICES_AUTHENTICATION_KEY_PASSWORD = "test".toCharArray();

    public static final char[] ALICES_PRIVATE_DECRYPTION_KEY_PASSWORD = "test".toCharArray();

    public static final String BOBS_PUBLIC_ENCRYPTION_TOKEN_FILE = BOB_FOLDER + "/bobs_public_key.etk";

    public static final String BOBS_PRIVATE_KEYSTORE_FILE = BOB_FOLDER + "/bobs_private_key_store.p12";

    public static final String BOBS_PRIVATE_KEYSTORE_TYPE = "PKCS12";

    public static final char[] BOBS_PRIVATE_KEYSTORE_PASSWORD = "test".toCharArray();

    public static final String BOBS_AUTHENTICATION_KEY_ALIAS = "Authentication";

    public static final char[] BOBS_AUTHENTICATION_KEY_PASSWORD = "test".toCharArray();

    public static final char[] BOBS_PRIVATE_DECRYPTION_KEY_PASSWORD = "test".toCharArray();

    /**
     * contains : "eHealth CA", "eHealth Root CA" rename to ehealthcacerts.jks
     */
    public static final String LOCAL_CA_CERTIFICATES_STORE_FILE = "/test_keys/ehealth_ca_cert_chain.jks";

    public static final String LOCAL_CA_CERTIFICATES_STORE_TYPE = "JKS";

    public static final char[] LOCAL_CA_CERTIFICATES_STORE_PASSWORD = "system".toCharArray();

    public static final String LOCAL_ROOTCA_CERTIFICATES_STORE_FILE = "/test_keys/ehealth_root_ca_cert.jks";

    public static final String LOCAL_ROOTCA_CERTIFICATES_STORE_TYPE = "JKS";

    public static final char[] LOCAL_ROOTCA_CERTIFICATES_STORE_PASSWORD = "system".toCharArray();

    public static final String BELGIUM_CA_CERTIFICATES_STORE_FILE = "/test_keys/belgium_ca_certs";

    private static KeyStore aliceKeyStore;

    private static Map<String, PrivateKey> aliceDecryptionKeys;

    private static KeyStore.PrivateKeyEntry aliceAuthKeyEntry;

    private static X509Certificate aliceAuthCert;

    private static EncryptionToken alicesEtk;

    private static PrivateKey alicesAuthKey;

    private static PrivateKey alicesDecryptKey;

    private static KeyStore bobKeyStore;

    private static Map<String, PrivateKey> bobDecryptionKeys;

    private static EncryptionToken bobsEtk;

    private static PrivateKey bobsAuthKey;

    private static PrivateKey bobsDecryptKey;

    private static KeyStore caCertificatesStore;

    private static KeyStore rootCaCertificatesStore;

    /** contains 'ehealth ca cert' */
    private static KeyStore ehealthCaCert;

    /** contains 'ehealth ca cert' and 'ehealth root ca cert' */
    private static KeyStore ehealthRootCaCert;

    static {
        aliceKeyStore = getAliceKeyStore();
        aliceDecryptionKeys = getAliceDecryptionKeys();
        alicesDecryptKey = getAliceDecryptionKey();
        alicesAuthKey = getAliceAuthenticationKey();

        bobKeyStore = getBobKeyStore();
        bobDecryptionKeys = getBobDecryptionKeys();
        bobsDecryptKey = getBobDecryptionKey();
        bobsAuthKey = getBobAuthKey();
    }

    private ExampleProperties() {
    }

    /**
     * @return Alice's Etk.
     */
    public static EncryptionToken getAliceEtk() {

        if (alicesEtk == null) {
            try {
                alicesEtk = EncryptionTokenFactory.getInstance().create(LOCAL_INSTANCE.getClass().getResourceAsStream(ALICES_PUBLIC_ENCRYPTION_TOKEN_FILE));
            } catch (Exception e) {
                throw new RuntimeException("Alice's ETK could not be initialised.");
            }
        }
        return alicesEtk;
    }


    /**
     * @return Alice's keystore
     */
    public static KeyStore getAliceKeyStore() {
        if (aliceKeyStore == null) {
            try {
                aliceKeyStore = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream(ALICES_PRIVATE_KEYSTORE_FILE), ALICES_PRIVATE_KEYSTORE_TYPE, ALICES_PRIVATE_KEYSTORE_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Alice's keystore could not be retrieved");
            }
        }
        return aliceKeyStore;

    }

    /**
     * @return the decryption keys of Alice.
     */
    public static Map<String, PrivateKey> getAliceDecryptionKeys() {
        if (aliceDecryptionKeys == null) {
            try {
                aliceDecryptionKeys = KeyManager.getDecryptionKeys(getAliceKeyStore(), ALICES_PRIVATE_DECRYPTION_KEY_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Alice's private keys could not be retrieved");
            }
        }
        return aliceDecryptionKeys;
    }

    /**
     * @return decryption key of Alice.
     */
    public static PrivateKey getAliceDecryptionKey() {
        if (alicesDecryptKey == null) {
            alicesDecryptKey = getAliceDecryptionKeys().values().iterator().next();
        }
        return alicesDecryptKey;
    }

    /**
     * @return authentication key of Alice.
     */
    public static PrivateKey getAliceAuthenticationKey() {
        if (alicesAuthKey == null) {
            try {
                alicesAuthKey = KeyManager.getKey(getAliceKeyStore(), ALICES_AUTHENTICATION_KEY_ALIAS, ALICES_AUTHENTICATION_KEY_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Alice's authentication key could not be retrieved.");
            }
        }
        return alicesAuthKey;
    }

    /**
     * @return private key and authentication certificate of Alice.
     */
    public static KeyStore.PrivateKeyEntry getAliceAuthKeyEntry() {
        if (aliceAuthKeyEntry == null) {
            try {
                aliceAuthKeyEntry = KeyManager.getKeyAndCertificates(getAliceKeyStore(), ALICES_AUTHENTICATION_KEY_ALIAS, ALICES_AUTHENTICATION_KEY_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Alice's authentication KeyEntry could not be retrieved.");
            }
        }
        return aliceAuthKeyEntry;
    }

    /**
     * @return X509Certificate Alice's authentication certificate.
     */
    public static X509Certificate getAliceAuthCertificate() {
        if (aliceAuthCert == null) {
            aliceAuthCert = KeyManager.getCertificate(getAliceKeyStore(), ALICES_AUTHENTICATION_KEY_ALIAS);
        }
        return aliceAuthCert;
    }


    /**
     * @return bob's key store.
     */
    public static KeyStore getBobKeyStore() {
        if (bobKeyStore == null) {
            try {
                bobKeyStore = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream(BOBS_PRIVATE_KEYSTORE_FILE), BOBS_PRIVATE_KEYSTORE_TYPE, BOBS_PRIVATE_KEYSTORE_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Bob's keystore could not be retrieved");
            }
        }
        return bobKeyStore;
    }


    /**
     * @return the decryption keys of Bob.
     */
    public static Map<String, PrivateKey> getBobDecryptionKeys() {
        if (bobDecryptionKeys == null) {
            try {
                bobDecryptionKeys = KeyManager.getDecryptionKeys(getBobKeyStore(), BOBS_PRIVATE_DECRYPTION_KEY_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Bob's decryption keys could not be retrieved");
            }
        }
        return bobDecryptionKeys;
    }

    /**
     * @return Bob's decryption key.
     */
    public static PrivateKey getBobDecryptionKey() {
        if (bobsDecryptKey == null) {
            bobsDecryptKey = getBobDecryptionKeys().values().iterator().next();
        }
        return bobsDecryptKey;
    }

    /**
     * @return Bob's authentication key.
     */
    public static PrivateKey getBobAuthKey() {
        if (bobsAuthKey == null) {
            try {
                bobsAuthKey = KeyManager.getKey(getBobKeyStore(), BOBS_AUTHENTICATION_KEY_ALIAS, BOBS_AUTHENTICATION_KEY_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("Bob's authentication key could not be retrieved");
            }
        }
        return bobsAuthKey;
    }

    /**
     * @return Bob's Etk.
     */
    public static EncryptionToken getBobEtk() {

        if (bobsEtk == null) {
            try {
                bobsEtk = EncryptionTokenFactory.getInstance().create(LOCAL_INSTANCE.getClass().getResourceAsStream(BOBS_PUBLIC_ENCRYPTION_TOKEN_FILE));
            } catch (Exception e) {
                throw new RuntimeException("Bob's ETK could not be initialised.");
            }
        }
        return bobsEtk;
    }


    /**
     * @return the tests and examples CA certificate store.
     */
    public static KeyStore getCaCertificatesStore() {

        if (caCertificatesStore == null) {
            try {
                caCertificatesStore = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream(ExampleProperties.LOCAL_CA_CERTIFICATES_STORE_FILE), ExampleProperties.LOCAL_CA_CERTIFICATES_STORE_TYPE, ExampleProperties.LOCAL_CA_CERTIFICATES_STORE_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("CA certs could not be initialised.");
            }
        }
        return caCertificatesStore;
    }

    /**
     * @return a key store with only the test Root CA certificate in it.
     */
    public static KeyStore getRootCaCertificatesStore() {
        if (rootCaCertificatesStore == null) {
            try {
                rootCaCertificatesStore = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream(ExampleProperties.LOCAL_ROOTCA_CERTIFICATES_STORE_FILE), ExampleProperties.LOCAL_ROOTCA_CERTIFICATES_STORE_TYPE, ExampleProperties.LOCAL_ROOTCA_CERTIFICATES_STORE_PASSWORD);
            } catch (Exception e) {
                throw new RuntimeException("CA certs could not be initialised.");
            }
        }
        return rootCaCertificatesStore;
    }

    /**
     * @return the ca certs
     */
    public static List<X509Certificate> getCaCertificates() {
        return KeyManager.getCertificates(getCaCertificatesStore());
    }

    /**
     * @return the CA cert
     */
    public static X509Certificate getCaCertificate() {
        return KeyManager.getCertificate(getEhealthCaCert(), "ehealth ca");
    }


    /**
     * @return base64 encoded KEK
     */
    public static String getBase64EncodedKek() {
        return "aaUnRynIwd3GFQmhXfW+VQ==";
    }

    /**
     * @return SecretKey
     */
    public static SecretKey getKek() {
        return SecretKeyUtils.getSecretKey(ExampleProperties.getBase64EncodedKek());
    }

    public static String getBase64EncodedKekId() {
        return "btSefztkXjZmlZyHQIumLA==";
    }


    public static KeyStore getEhealthCaCert() {
        if (ehealthCaCert == null) {
            try {
                ehealthCaCert = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream("/test_keys/ehealth_ca_cert.jks"), "JKS", "system".toCharArray());
            } catch (Exception e) {
                throw new RuntimeException("keystore could not be initialised.");
            }
        }
        return ehealthCaCert;
    }

    public static KeyStore getEhealthRootCaCert() {
        if (ehealthRootCaCert == null) {
            try {
                ehealthRootCaCert = KeyManager.getKeyStore(ExampleProperties.class.getResourceAsStream("/test_keys/ehealth_root_ca_cert.jks"), "JKS", "system".toCharArray());
            } catch (Exception e) {
                throw new RuntimeException("keystore could not be initialised.");
            }
        }
        return ehealthRootCaCert;
    }

    public static X509Certificate getAliceCaCert() {
        return getCaCertificate();
    }
}
