package cn.quantgroup.customer.config.rest;

import cn.quantgroup.tech.brave.service.ITechHttpClient;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BestMatchSpecFactory;
import org.apache.http.impl.cookie.BrowserCompatSpec;
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import static cn.quantgroup.customer.constant.Constant.HTTP;
import static cn.quantgroup.customer.constant.Constant.HTTPS;

@Configuration
public class RestConfig {

    private static final int SO_TIME_OUT = 60 * 1000;
    private static final int TIME_OUT = 15 * 1000;

    @Autowired
    @Qualifier("techHttpClient")
    private ITechHttpClient techHttpClient;

    @Bean("commonRestTemplate")
    public RestTemplate sslRestTemplate() {
        SSLContext sslContext;
        try {
            sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(null, (TrustStrategy) (x509Certificates, s) -> true).build();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HTTP, new PlainConnectionSocketFactory())
                .register(HTTPS, sslsf)
                .build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
        connectionManager.setMaxTotal(1000);
        connectionManager.setDefaultMaxPerRoute(1000);
        int connectTimeout = 15000;
        int soTimeout = 60000;
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(connectTimeout).setConnectionRequestTimeout(connectTimeout)
                .setSocketTimeout(soTimeout).build();

        HttpClientBuilder clientBuilder = HttpClientBuilder.create();
        clientBuilder.setSSLSocketFactory(sslsf);
        clientBuilder.setDefaultRequestConfig(requestConfig);
        clientBuilder.setConnectionManager(connectionManager);
        // 开启重试 状态 503 重试
        clientBuilder.setServiceUnavailableRetryStrategy(new DefaultServiceUnavailableRetryStrategy(1, 500));
        // 开启重试
        clientBuilder.setRetryHandler(new HttpRequestRetryHandler());
        HttpClient httpClient = clientBuilder.build();

        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
        clientHttpRequestFactory.setHttpClient(httpClient);
        clientHttpRequestFactory.setConnectTimeout(connectTimeout);
        int readTimeout = 30000;
        clientHttpRequestFactory.setReadTimeout(readTimeout);

        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        restTemplate.setErrorHandler(new HttpResponseErrorHandler());
        restTemplate.getInterceptors().add(new LogInterceptor());
        return restTemplate;
    }

    /**
     * 日志输出
     */
    public static class LogInterceptor implements ClientHttpRequestInterceptor {
        private final static Logger log = LoggerFactory.getLogger(RestTemplate.class);
        private final static int MAX_LEN = 1024;

        @Override
        public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
            String p = null;
            if (body != null) {
                if (body.length > MAX_LEN) {
                    p = "Too long.";
                } else {
                    p = new String(body);
                }
            }
            log.info("HTTP Request: method={},uri={},param:{}", request.getMethod(), request.getURI(), p);
            return execution.execute(request, body);
        }
    }

    @Bean(name = "httpClient")
    public CloseableHttpClient httpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        //  socket factory
        ConnectionSocketFactory plainSocketFactory = new PlainConnectionSocketFactory();
        SSLContext sslContext = new org.apache.http.conn.ssl.SSLContextBuilder().loadTrustMaterial(
                null, (x509Certificates, authType) -> true).build();

        LayeredConnectionSocketFactory sslSocketFactory =
                new SSLConnectionSocketFactory(sslContext, new AllowAllHostnameVerifier());
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", plainSocketFactory)
                .register("https", sslSocketFactory).build();

        // cookie specification
        Registry<CookieSpecProvider> cookieSpecProviderRegistry = RegistryBuilder.<CookieSpecProvider>create()
                .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
                .register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
                .register("easy", httpContext -> new BrowserCompatSpec() {
                    public void validate(Cookie cookie, CookieOrigin origin) {
                    }
                }).build();

        // connection manager
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

        connectionManager.setMaxTotal(10000);
        connectionManager.setDefaultMaxPerRoute(1000);

        // retry handler
        org.apache.http.client.HttpRequestRetryHandler retryHandler = new StandardHttpRequestRetryHandler(3, false);

        // keep alive strategy
        ConnectionKeepAliveStrategy keepAliveStrategy = new DefaultConnectionKeepAliveStrategy();

        // httpclient
        return techHttpClient.createHttpClientBuilder()
                .setConnectionManager(connectionManager)
                .setRetryHandler(retryHandler)
                .setKeepAliveStrategy(keepAliveStrategy).setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectTimeout(TIME_OUT)
                        .setSocketTimeout(SO_TIME_OUT)
                        .setConnectionRequestTimeout(TIME_OUT)
                        .build())
                .setDefaultCookieSpecRegistry(cookieSpecProviderRegistry).build();
    }

}
