/*
 * Copyright (c) 2004-2017, by Alex Leigh.
 * All rights reserved.
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE
 * The copyright notice above does not evidence any
 * actual or intended publication of such source code.
 */

package lc.mecha.http.client;

import lc.mecha.security.CertificateKeyStore;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.ssl.SSLContexts;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

/**
 * This class standardizes creation of <code>HttpClient</code> instances, and works around some peculiar limitations
 * in the API.
 *
 * @author Alex Leigh
 */
public class HttpClientRig {
    private final static String userAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; WOW64; Trident/7.0; .NET4.0E; .NET4.0C; .NET CLR 3.5.30729; .NET CLR 2.0.50727; .NET CLR 3.0.30729; InfoPath.3)";
    private final CloseableHttpClient client;
    private final BasicCookieStore cookieStore;

    private final CertificateKeyStore certificateKeyStore;

    public HttpClientRig() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        this(false, null);
    }

    // TODO: There are actually two variations on the SSL issue, one is self-signed and the other is allow any. They
    // TODO: should probably be different levels of checks.
    public HttpClientRig(boolean ignoreSslCertErrors, CertificateKeyStore certificateKeyStore)
            throws NoSuchAlgorithmException, KeyStoreException,
            KeyManagementException {
        cookieStore = new BasicCookieStore();
        var header = new BasicHeader(HttpHeaders.USER_AGENT, userAgent);
        List<Header> headers = new ArrayList<>();
        headers.add(header);
        this.certificateKeyStore = certificateKeyStore;

        if (ignoreSslCertErrors) {
            var builder = new SSLContextBuilder();
            builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
            var sslSocketFactory = new SSLConnectionSocketFactory(
                    builder.build(), SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            client = HttpClients.custom().setSSLSocketFactory(
                    sslSocketFactory).setDefaultHeaders(headers).setDefaultCookieStore(cookieStore).build();
        } else {
            if (certificateKeyStore != null) {
                final var sslContext = SSLContexts.custom()
                        .loadTrustMaterial(certificateKeyStore.getKeyStore(), null)
                        .build();
                client = HttpClients.custom().setDefaultHeaders(headers).setSSLContext(sslContext)
                        .setDefaultCookieStore(cookieStore).build();
            } else {
                client = HttpClients.custom().setDefaultHeaders(headers).setDefaultCookieStore(cookieStore).build();
            }
        }
    }

    /**
     * Return the <code>HttpClient</code> associated with this rig.
     *
     * @return
     */
    public CloseableHttpClient getClient() {
        return client;
    }

    /**
     * Return the <code>CookieStore</code> associated with this rig.
     *
     * @return
     */

    public BasicCookieStore getCookieStore() {
        return cookieStore;
    }

    /**
     * Set the given cookie into the cookie store for this setup.
     *
     * @param name
     * @param value
     */
    public void setCookie(String name, String value) {
        var cookie = new BasicClientCookie(name, value);
        cookieStore.addCookie(cookie);
    }

    public CertificateKeyStore getCertificateKeyStore() {
        return certificateKeyStore;
    }
}