package com.criticalblue.reactnative;

import android.util.Base64;
import com.facebook.react.modules.network.OkHttpClientFactory;
import com.facebook.react.modules.network.OkHttpClientProvider;
import com.facebook.react.modules.network.ReactCookieJarContainer;

import java.io.ByteArrayInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.*;

import javax.net.ssl.*;

import android.util.Log;

public class PinnedClientFactory implements OkHttpClientFactory {
    private CertificatePinner certificatePinner;
    private List<DomainProperty> domainProperties;
    private List<String> selfSignedCerts;

    public PinnedClientFactory(CertificatePinner certificatePinner, List<DomainProperty> domainProperties, List<String> selfSignedCerts) {
        this.certificatePinner = certificatePinner;
        this.domainProperties = domainProperties;
        this.selfSignedCerts = selfSignedCerts;
    }

    @Override
    public OkHttpClient createNewNetworkModuleClient() {
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            @Override
            public X509Certificate[] getAcceptedIssuers() {
//                X509Certificate[] cArrr = new X509Certificate[0];
//                return cArrr;

                TrustManagerFactory trustManagerFactory;
                TrustManager[] trustManagers = null;
                try {
                    trustManagerFactory = TrustManagerFactory.getInstance(
                            TrustManagerFactory.getDefaultAlgorithm());
                    trustManagerFactory.init((KeyStore) null);
                    trustManagers = trustManagerFactory.getTrustManagers();
                    if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                        throw new IllegalStateException("Unexpected default trust managers:"
                                + Arrays.toString(trustManagers));
                    }
                } catch (Exception e) {
                    Log.e("ADInterceptor", "Error: " + e.getMessage());
                }
                if (trustManagers == null) {
                    Log.v("ADInterceptor", "No Trust Manager");
                    return new X509Certificate[0];
                }
                final X509TrustManager trustManager = (X509TrustManager) trustManagers[0];

                CertificateFactory fact = null;
                List<X509Certificate> certs = new ArrayList<>();
                try {
                    fact = CertificateFactory.getInstance("X.509");
                    for (String cert : selfSignedCerts) {
                        byte[] byteKey = Base64.decode(cert.getBytes(), Base64.DEFAULT);
                        certs.add((X509Certificate) fact.generateCertificate(new ByteArrayInputStream(byteKey)));
                    }
                } catch (Exception e) {
                    Log.i("ADInterceptor", "Error in creating new cert " + e.getMessage());
                }
                X509Certificate[] issuers = trustManager.getAcceptedIssuers();
                X509Certificate[] trustedCerts = trustManager.getAcceptedIssuers();
                if (!certs.isEmpty()) {
                    trustedCerts = new X509Certificate[trustedCerts.length + certs.size()];
                    if (issuers.length >= 0) {
                        System.arraycopy(issuers, 0, trustedCerts, 0, issuers.length);
                    }
                    for (X509Certificate cer : certs) {
                        trustedCerts[issuers.length] = cer;
                    }
                }
                Log.i("ADInterceptor", "Getting All Issuers, Count = " + trustedCerts.length);
                return trustedCerts;
            }

            @Override
            public void checkServerTrusted(final X509Certificate[] chain,
                                           final String authType) throws CertificateException {
                Log.i("ADInterceptor", "Check server trusted");
            }

            @Override
            public void checkClientTrusted(final X509Certificate[] chain,
                                           final String authType) throws CertificateException {
                Log.i("ADInterceptor", "Check client trusted");
            }
        }};
        OkHttpClient.Builder client = null;
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                            Log.i("ADInterceptor", "Verifying hostname");
                            return true;
                        }
                    })
                    .connectTimeout(0, TimeUnit.MILLISECONDS)
                    .readTimeout(0, TimeUnit.MILLISECONDS)
                    .writeTimeout(0, TimeUnit.MILLISECONDS)
                    .cookieJar(new ReactCookieJarContainer());
        } catch (Exception e) {
            Log.i("ADInterceptor", "Error: " + e.toString());
            e.printStackTrace();
        }

        if (certificatePinner != null && client != null) {
            client.addNetworkInterceptor(new LoggingInterceptor(certificatePinner, domainProperties));
        } else {
            Log.i("ADInterceptor", "Certificate Pinner or Client is null");
        }

        return OkHttpClientProvider.enableTls12OnPreLollipop(client).build();
    }
}

// end of file
