package ru.yandex.qe.ssl;

import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.CRL;
import java.security.cert.CertStore;
import java.security.cert.Certificate;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.AbstractCollection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import com.google.common.base.Throwables;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;

public class SslUtils {
    private SslUtils() {}

    public static String getCN(Principal value) {
        X500Name x500name = new X500Name(value.getName());
        RDN cn = x500name.getRDNs(BCStyle.CN)[0];
        return IETFUtils.valueToString(cn.getFirst().getValue());
    }

    public static TrustManagerFactory trustManagerFactory(List<X509Certificate> certificates, Consumer<PKIXBuilderParameters> revocationInitializer) throws Exception {
        final KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, "".toCharArray());
        for (X509Certificate c : certificates) {
            trustStore.setCertificateEntry(c.getSubjectDN().getName(), c);
        }
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        PKIXBuilderParameters params = new PKIXBuilderParameters(trustStore, new X509CertSelector());
        revocationInitializer.accept(params);
        tmf.init(new CertPathTrustManagerParameters(params));
        return tmf;
    }

    public static CertStore fetchingCertStore(final CrlFetcher fetcher) {
        try {
            return CertStore.getInstance("Collection", new CollectionCertStoreParameters(new AbstractCollection<CRL>() {
                @Override
                public Iterator<CRL> iterator() {
                    return fetcher.getCrls().iterator();
                }

                @Override
                public int size() {
                    throw new UnsupportedOperationException();
                }
            }));
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }
    }

    public static KeyManagerFactory keyManagerFactory(PrivateKey key, X509Certificate cert) throws Exception {
        final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null, "".toCharArray());
        keyStore.setKeyEntry("key", key, "".toCharArray(), new Certificate[]{cert});
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(keyStore, "".toCharArray());
        return kmf;
    }

    public static SSLContext sslContext(KeyManagerFactory kmf, TrustManagerFactory tmf) {
        try {
            SSLContext serverContext = SSLContext.getInstance("TLS");
            serverContext.init(
                    kmf.getKeyManagers(),
                    tmf.getTrustManagers(), null);
            return serverContext;
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }
    }
}
