package com.yihu.hos.http;

import org.apache.http.auth.AuthScope;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * @created Airhead 2016/8/24.
 */
public class HttpClientKit {
    private static final String HTTP = "http";
    private static final String HTTPS = "https";
    private static Map<String, SSLConnectionSocketFactory> sslConnectionSocketFactoryMap = new HashMap<>();
    private static Map<String, CredentialsProvider> credentialsProviderMap = new HashMap<>();

    public static HTTPClient use(String url) {
        try {
            URL urlUse = new URL(url);
            String protocol = urlUse.getProtocol();
            String host = urlUse.getHost();
            int port = urlUse.getPort();
            if (protocol.equals(HTTPS)) {
                return getHttpsClient(host, port);
            } else if (protocol.equals(HTTP)) {
                return getHttpClient(host, port);
            }
        } catch (MalformedURLException ignored) {
        }

        return new HTTPClientImpl();
    }

    /**
     * 用于无认证和受信证书
     * @return HTTPClient
     */
    public static HTTPClient getHttpClient() {
        return new HTTPClientImpl();
    }

    private static HTTPClient getHttpClient(String host, Integer port) {
        String key = host + ":" + port;
        CredentialsProvider credentialsProvider = credentialsProviderMap.get(key);
        if (credentialsProvider == null) {
            return new HTTPClientImpl();
        }

        return new HTTPClientImpl(credentialsProvider);
    }

    private static HTTPClient getHttpsClient(String host, Integer port) {
        String key = host + ":" + port;
        SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryMap.get(key);
        if (sslConnectionSocketFactory == null) {
            throw new IllegalArgumentException("Custom SSL access must add trust store first.");
        }

        CredentialsProvider credentialsProvider = credentialsProviderMap.get(key);
        if (credentialsProvider == null) {
            return new HTTPSClientImpl(sslConnectionSocketFactory);
        }

        return new HTTPSClientImpl(sslConnectionSocketFactory, credentialsProvider);
    }

    public static void addTrustStore(String host, Integer port, String trustStorePath, String password) throws Exception {
        if (host == null || port == null || trustStorePath == null || password == null) {
            throw new IllegalArgumentException("must init basic credential first.");
        }

        SSLConnectionSocketFactory sslConnectionSocketFactory = sslConnectionSocketFactoryMap.get(host);
        if (sslConnectionSocketFactory != null) {
            return;
        }

        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(new File(trustStorePath), password.toCharArray(),
                        new TrustSelfSignedStrategy())
                .build();

        // Allow TLSv1 protocol only
        sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1"},
                null,
                new AllowAllHostnameVerifier());

        sslConnectionSocketFactoryMap.put(host, sslConnectionSocketFactory);
    }

    public static void addBasicCredentials(String host, Integer port, String user, String password) {
        if (host == null || port == null || user == null || password == null) {
            throw new IllegalArgumentException("must init trust store first.");
        }

        String key = host + ":" + port;
        if (credentialsProviderMap.containsKey(key)) {
            return;
        }

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(new AuthScope(host, port),
                new org.apache.http.auth.UsernamePasswordCredentials(user, password));

        credentialsProviderMap.put(key, credentialsProvider);
    }

    public static HTTPResponse get(String url) {
        return use(url).get(url);
    }

    public static HTTPResponse get(String url, Map<String, String> params) {
        return use(url).get(url, params);
    }

    public static HTTPResponse get(String url, Map<String, String> params, Map<String, String> headers) {
        return use(url).get(url, params, headers);
    }

    public static HTTPResponse post(String url) {
        return use(url).post(url);
    }

    public static HTTPResponse post(String url, Map<String, String> params) {
        return use(url).post(url, params);
    }

    public static HTTPResponse post(String url, Map<String, String> params, Map<String, String> headers) {
        return use(url).post(url, params, headers);
    }

    public static HTTPResponse postFile(String url, String path) {
        return use(url).postFile(url, path);
    }

    public static HTTPResponse postFile(String url, String path, Map<String, String> params) {
        return use(url).postFile(url, path, params);
    }

    public static HTTPResponse postFile(String url, String path, Map<String, String> params, Map<String, String> headers) {
        return use(url).postFile(url, path, params, headers);
    }

    public static HTTPResponse put(String url) {
        return use(url).put(url);
    }

    public static HTTPResponse put(String url, Map<String, String> params) {
        return use(url).put(url, params);
    }

    public static HTTPResponse put(String url, Map<String, String> params, Map<String, String> headers) {
        return use(url).put(url, params, headers);
    }

    public static HTTPResponse delete(String url) {
        return use(url).delete(url);
    }

    public static HTTPResponse delete(String url, Map<String, String> params) {
        return use(url).delete(url, params);
    }

    public static HTTPResponse delete(String url, Map<String, String> params, Map<String, String> headers) {
        return use(url).delete(url, params, headers);
    }

    public static HTTPResponse request(String method, String url, Map<String, String> params, Map<String, String> headers) {
        return use(url).request(method, url, params, headers);
    }
}
