package org.holer.common.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class SSLCreator {
    private static Logger log = LoggerFactory.getLogger(SSLCreator.class);

    private HolerConfig config;

    private SSLContext sslCtx;

    private String jksPath;

    private static SSLCreator creator = null;


    public static SSLCreator getCreator() {
        if (null == creator) {
            creator = new SSLCreator();
        }
        return creator;
    }


    public static SSLCreator getCreator(String jksPath) {
        if (null == creator) {
            creator = new SSLCreator(jksPath);
        }
        return creator;
    }


    public SSLCreator() {
        this.config = HolerConfig.getConfig();
        this.sslCtx = null;
        this.jksPath = null;
        this.sslCtx = init();
    }


    public SSLCreator(String jksPath) {
        this.config = HolerConfig.getConfig();
        this.sslCtx = null;
        this.jksPath = null;
        this.jksPath = jksPath;
        this.sslCtx = init();
    }


    public SSLContext getSSLContext() {
        return this.sslCtx;
    }


    private InputStream loadJks(String jksPath) throws FileNotFoundException {
        ClassLoader loader = SSLCreator.class.getClassLoader();
        URL ju = loader.getResource(jksPath);
        if (null != ju) {

            log.info("Starting with jks file: {}.", jksPath);
            return loader.getResourceAsStream(jksPath);
        }

        log.warn("No keystore has been found in the bundled resources. Scanning filesystem...");
        File jf = new File(jksPath);
        if (jf.exists()) {

            log.info("Loading external keystore. jks file: {}.", jksPath);
            return new FileInputStream(jf);
        }

        log.warn("The keystore file does not exist. jks file: {}.", jksPath);
        return null;
    }


    public SSLContext init() {
        String jks = this.config.strValue("HOLER_SSL_JKS", "org/holer/conf/holer.jks");
        if (StringUtils.isNotBlank(this.jksPath)) {
            jks = this.jksPath;
        }


        String sslPaswd = this.config.strValue("HOLER_SSL_PASSWORD", "Wisdom-Holer");


        try {
            log.info("Loading keystore. Keystore path: {}.", jks);
            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(loadJks(jks), sslPaswd.toCharArray());

            log.info("Initializing key manager...");
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, sslPaswd.toCharArray());


            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(ks);


            log.info("Initializing SSL context...");
            SSLContext sslCtx = SSLContext.getInstance("TLS");
            sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            log.info("The SSL context has been initialized successfully.");

            return sslCtx;
        } catch (Exception e) {

            log.error("Unable to initialize SSL context. Cause: {}, error message: {}.", e.getCause(), e.getMessage());
            return null;
        }
    }
}
