package com.m2pool.pool.config;

import lombok.Data;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

/**
 * Apache HttpClient 连接池工具类
 */
public class ApacheHttpClientPoolUtil {

    // 连接池管理器
    private static final PoolingHttpClientConnectionManager connectionManager;
    // 请求配置
    private static final RequestConfig requestConfig;
    // 单例的 CloseableHttpClient
    private static final CloseableHttpClient httpClient;

    // 最大连接数
    private static final int MAX_TOTAL = 100;
    // 每个路由的最大连接数
    private static final int MAX_PER_ROUTE = 10;
    // 连接超时时间，单位毫秒
    private static final int CONNECT_TIMEOUT = 3000;
    // 请求获取数据的超时时间，单位毫秒
    private static final int SOCKET_TIMEOUT = 5000;
    // 从连接池获取连接的超时时间，单位毫秒
    private static final int CONNECTION_REQUEST_TIMEOUT = 3000;

    static {
        try {
            // 初始化连接池管理器
            connectionManager = new PoolingHttpClientConnectionManager(
                    getSocketFactoryRegistry(),
                    null,
                    null,
                    null,
                    30, TimeUnit.SECONDS
            );
            // 设置最大连接数
            connectionManager.setMaxTotal(MAX_TOTAL);
            // 设置每个路由的最大连接数
            connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);

            // 初始化请求配置
            requestConfig = RequestConfig.custom()
                    .setConnectTimeout(CONNECT_TIMEOUT)
                    .setSocketTimeout(SOCKET_TIMEOUT)
                    .setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT)
                    .build();

            // 初始化单例的 CloseableHttpClient
            httpClient = HttpClients.custom()
                    .setConnectionManager(connectionManager)
                    .setDefaultRequestConfig(requestConfig)
                    .evictIdleConnections(60, TimeUnit.SECONDS)
                    .build();
        } catch (Exception e) {
            throw new RuntimeException("初始化 HttpClient 连接池失败", e);
        }
    }

    /**
     * 获取支持 HTTP 和 HTTPS 的 Socket 工厂注册器
     * @return Socket 工厂注册器
     * @throws NoSuchAlgorithmException 当指定的算法不可用时抛出
     * @throws KeyManagementException 当密钥管理操作失败时抛出
     * @throws KeyStoreException 当密钥库操作失败时抛出
     */
    private static Registry<ConnectionSocketFactory> getSocketFactoryRegistry()
            throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        }).build();
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslContext, NoopHostnameVerifier.INSTANCE);

        return RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();
    }

    /**
     * 获取单例的 CloseableHttpClient 实例
     * @return CloseableHttpClient 实例
     */
    public static CloseableHttpClient getHttpClient() {
        return httpClient;
    }
}
