package com.eactive.eai.adapter.http.secure;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.codec.binary.StringUtils;

import com.eactive.eai.util.TestModeChecker;

public class SSLContextFactory {

	private static boolean testMode = TestModeChecker.isTestMode();

	private SSLContextFactory() {

	}

	// not used (파일인증서 관리시 사용)
	public static SSLContext createMTLSContextFromFile(String type, String keyStorePath, String keyStorePassword,
			String trustStorePath, String trustStorePassword, boolean skipTrust, String[] tlsVersions,
			String[] cipherSuites) throws SSLContextCreateException {
		try {
			KeyStore keyStore = KeyStore.getInstance(type);
			try (FileInputStream keyStoreInput = new FileInputStream(new File(keyStorePath))) {
				keyStore.load(keyStoreInput, keyStorePassword.toCharArray());
			}

			KeyStore trustStore = KeyStore.getInstance(type);
			try (FileInputStream trustStoreInput = new FileInputStream(new File(trustStorePath))) {
				trustStore.load(trustStoreInput, trustStorePassword.toCharArray());
			}

			KeyManagerFactory keyManagerFactory = KeyManagerFactory
					.getInstance(KeyManagerFactory.getDefaultAlgorithm());
			keyManagerFactory.init(keyStore, trustStorePassword.toCharArray());

			SSLContext sslContext = SSLContext.getInstance("TLS");
			if (skipTrust) {
				if (testMode) {
					TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
						public java.security.cert.X509Certificate[] getAcceptedIssuers() {
							return null;
						}

						public void checkClientTrusted(X509Certificate[] certs, String authType) {
						}

						public void checkServerTrusted(X509Certificate[] certs, String authType) {
						}
					} };
					sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null);
				} else {
					// java root CA를 사용함 (jvm 설정에 추가되어야 함)
					TrustManagerFactory trustManagerFactory = TrustManagerFactory
							.getInstance(TrustManagerFactory.getDefaultAlgorithm());
					trustManagerFactory.init((KeyStore) null);
					sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
				}
			} else {
				TrustManagerFactory trustManagerFactory = TrustManagerFactory
						.getInstance(TrustManagerFactory.getDefaultAlgorithm());
				trustManagerFactory.init(trustStore);

				sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
			}

			SSLParameters sslParameters = sslContext.getDefaultSSLParameters();
			if (tlsVersions != null) {
				sslParameters.setProtocols(tlsVersions);
			}
			if (cipherSuites != null) {
				sslParameters.setCipherSuites(cipherSuites);
			}

			return sslContext;
		} catch (Exception e) {
			String errorMessage = String.format("Failed to create SSLContext type(Algorithm): %s", type);
			throw new SSLContextCreateException(errorMessage, e);
		}
	}

	public static SSLContext createMTLSContextFromContent(String type, String keyStoreBase64Content,
			String keyStorePassword, String trustStoreBase64Content, String trustStorePassword, boolean skipTrust,
			String[] tlsVersions, String[] cipherSuites) throws SSLContextCreateException {
		try {
			KeyStore keyStore = KeyStore.getInstance(type);
			byte[] keyBytes = Base64.getDecoder().decode(keyStoreBase64Content);
			try (ByteArrayInputStream bis = new ByteArrayInputStream(keyBytes)) {
				keyStore.load(bis, keyStorePassword.toCharArray());
			}

			KeyStore trustStore = KeyStore.getInstance(type);
			byte[] trustBytes = Base64.getDecoder().decode(trustStoreBase64Content);
			try (ByteArrayInputStream bis = new ByteArrayInputStream(trustBytes)) {
				trustStore.load(bis, trustStorePassword.toCharArray());
			}

			KeyManagerFactory keyManagerFactory = KeyManagerFactory
					.getInstance(KeyManagerFactory.getDefaultAlgorithm());
			keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());

			SSLContext sslContext = SSLContext.getInstance("TLS");
			if (skipTrust) {
				if (testMode) {
					TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
						public java.security.cert.X509Certificate[] getAcceptedIssuers() {
							return null;
						}

						public void checkClientTrusted(X509Certificate[] certs, String authType) {
						}

						public void checkServerTrusted(X509Certificate[] certs, String authType) {
						}
					} };
					sslContext.init(keyManagerFactory.getKeyManagers(), trustAllCerts, null);
				} else {
					// java root CA를 사용함 (jvm 설정에 추가되어야 함)
					TrustManagerFactory trustManagerFactory = TrustManagerFactory
							.getInstance(TrustManagerFactory.getDefaultAlgorithm());
					trustManagerFactory.init((KeyStore) null);
					sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
				}
			} else {

				TrustManagerFactory trustManagerFactory = TrustManagerFactory
						.getInstance(TrustManagerFactory.getDefaultAlgorithm());
				trustManagerFactory.init(trustStore);
				sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
			}

			SSLParameters sslParameters = sslContext.getDefaultSSLParameters();
			if (tlsVersions != null) {
				sslParameters.setProtocols(tlsVersions);
			}
			if (cipherSuites != null) {
				sslParameters.setCipherSuites(cipherSuites);
			}
			return sslContext;
		} catch (Exception e) {
			String errorMessage = String.format("Failed to create SSLContext type(Algorithm): %s", type);
			throw new SSLContextCreateException(errorMessage, e);
		}
	}

	public static SSLContext createMTLSContextForJKS(String keyStorePath, String keyStorePassword,
			String trustStorePath, String trustStorePassword, String[] tlsVersions, String[] cipherSuites)
			throws SSLContextCreateException {
		return createMTLSContextFromFile("JKS", keyStorePath, keyStorePassword, trustStorePath, trustStorePassword,
				false, tlsVersions, cipherSuites);
	}

	public static SSLContext createMTLSContextForP12(String keyStorePath, String keyStorePassword,
			String trustStorePath, String trustStorePassword, String[] tlsVersions, String[] cipherSuites)
			throws SSLContextCreateException {
		return createMTLSContextFromFile("PKCS12", keyStorePath, keyStorePassword, trustStorePath, trustStorePassword,
				false, tlsVersions, cipherSuites);
	}
}
