package com.malk.pake.utils;


import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.impl.io.DefaultHttpResponseParserFactory;
import org.apache.http.util.EntityUtils;

import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * http请求封装
 *
 * @author lh
 * @date 2022-09-23
 */
@Slf4j
public class HttpUtils {

    /**
     * 默认请求类型
     */
    private static final String DEFAULT_CONTENT_TYPE = "application/json";

    /**
     * 默认编码
     */
    private static final String DEFAULT_ENCODING = "utf-8";

    /**
     * GBK编码
     */
    private static final String GBK_ENCODING = "GBK";

    /**
     * 默认超时时间
     */
    private static final int DEFAULT_TIME_OUT = 60;

    private static PoolingHttpClientConnectionManager manager = null;

    private static CloseableHttpClient httpClient = null;

    /**
     * 获取httpclient对象
     *
     * @return
     */
    public static synchronized CloseableHttpClient getHttpClient(int timeOut) {
        if (null == httpClient) {
            log.info("============================================创建");
            // 注册访问协议相关的socket工厂
            Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.INSTANCE)
                    .register("https", SSLConnectionSocketFactory.getSystemSocketFactory())
                    .build();

            // httpConnection工厂
            HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionFactory =
                    new ManagedHttpClientConnectionFactory(DefaultHttpRequestWriterFactory.INSTANCE
                            , DefaultHttpResponseParserFactory.INSTANCE);

            // DNS解析器
            DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
            // 创建池化连接管理器
            manager = new PoolingHttpClientConnectionManager(socketFactoryRegistry, connectionFactory, dnsResolver);
            // socket配置
            SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
            manager.setDefaultSocketConfig(socketConfig);
            // 连接池最大连接数
            manager.setMaxTotal(300);
            manager.setDefaultMaxPerRoute(200);
            // 5s检测一次连接池连接
            manager.setValidateAfterInactivity(5 * 1000);
            // 默认请求配置
            RequestConfig requestConfig = RequestConfig.custom()
                    // 连接超时时间
                    .setConnectTimeout(timeOut * 1000)
                    // 等待数据超时时间
                    .setSocketTimeout(timeOut * 1000)
                    // 从连接池获取连接超时时间
                    .setConnectionRequestTimeout(timeOut * 1000)
                    .build();
            // 创建httpclient
            httpClient = HttpClients.custom()
                    .setConnectionManager(manager)
                    // 连接池不共享
                    .setConnectionManagerShared(false)
                    // 定期回收空闲连接
                    .evictIdleConnections(timeOut, TimeUnit.SECONDS)
                    // 定期回收过期连接
                    .evictExpiredConnections()
                    .setConnectionTimeToLive(timeOut, TimeUnit.SECONDS)
                    // 设置默认请求
                    .setDefaultRequestConfig(requestConfig)
                    // 连接重用策略
                    .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                    // 长连接配置
                    .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                    // 设置重试次数
                    .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                    .build();

            // jvm停止或重启时，关闭连接池释放连接
            Runtime.getRuntime().addShutdownHook(new Thread() {

                @Override
                public void run() {
                    try {
                        if (null != httpClient) {
                            httpClient.close();
                        }
                    } catch (Exception e) {
                        log.error("httpclient关闭异常：{}", e);
                    }
                }
            });
        }
        return httpClient;
    }

    /**
     * http请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpPost(String url, Map params,Map<String,String> headers) {
        log.info("POST请求url：{}", url);
        log.info("POST请求参数:{}",JSON.toJSONString(params));
        CloseableHttpClient httpClient = getHttpClient(DEFAULT_TIME_OUT);
        HttpPost httpPost = new HttpPost(url);

        if (null != headers){
            for (String key:headers.keySet()){
                httpPost.setHeader(key,headers.get(key).toString());
            }
        }

        String json = JSON.toJSONString(params);
        StringEntity stringEntity = new StringEntity(json, DEFAULT_ENCODING);
        stringEntity.setContentEncoding(DEFAULT_ENCODING);
        httpPost.setEntity(stringEntity);
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(httpPost);) {
            HttpEntity entity = response.getEntity();
            if (null != entity) {
                result = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
                EntityUtils.consume(entity);
            }
        } catch (Exception e) {
            log.error("接口请求异常：{}", e);
        }
        return result;
    }

    /**
     * http请求(POST请求，GBK编码)
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpPostGBK(String url, Map params,Map<String,String> headers) {
        log.info("POST请求url：{}", url);
        log.info("POST请求参数:{}",JSON.toJSONString(params));
        CloseableHttpClient httpClient = getHttpClient(DEFAULT_TIME_OUT);
        HttpPost httpPost = new HttpPost(url);

        if (null != headers){
            for (String key:headers.keySet()){
                httpPost.setHeader(key,headers.get(key).toString());
            }
        }

        String json = JSON.toJSONString(params);
        StringEntity stringEntity = new StringEntity(json, DEFAULT_ENCODING);
        stringEntity.setContentEncoding(DEFAULT_ENCODING);
        httpPost.setEntity(stringEntity);
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(httpPost);) {
            HttpEntity entity = response.getEntity();
            if (null != entity) {
//                result = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
                result = EntityUtils.toString(entity, Charset.defaultCharset());
                EntityUtils.consume(entity);
            }
        } catch (Exception e) {
            log.error("接口请求异常：{}", e);
        }
        return result;
    }

    /**
     * http请求(POST请求，GBK编码)
     *
     * @param url
     * @param params
     * @param headers
     * @param timeOut 超时时间
     * @return
     */
    public static String httpPostGBK(String url, Map params, Map<String, String> headers, int timeOut) {
        log.info("POST请求url：{}", url);
        log.info("POST请求参数:{}", JSON.toJSONString(params));
        CloseableHttpClient httpClient = getHttpClient(timeOut);
        HttpPost httpPost = new HttpPost(url);

        if (null != headers) {
            for (String key : headers.keySet()) {
                httpPost.setHeader(key, headers.get(key).toString());
            }
        }

        String json = JSON.toJSONString(params);
        StringEntity stringEntity = new StringEntity(json, DEFAULT_ENCODING);
        stringEntity.setContentEncoding(DEFAULT_ENCODING);
        httpPost.setEntity(stringEntity);
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(httpPost);) {
            HttpEntity entity = response.getEntity();
            if (null != entity) {
//                result = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
                result = EntityUtils.toString(entity, Charset.defaultCharset());
                EntityUtils.consume(entity);
            }
        } catch (Exception e) {
            log.error("接口请求异常：{}", e);
        }
        return result;
    }

    /**
     * http请求
     *
     * @param url
     * @return
     */
    public static String httpGet(String url) {
        log.info("请求url：{}", url);
        CloseableHttpClient httpClient = getHttpClient(DEFAULT_TIME_OUT);
        HttpGet httpGet = new HttpGet(url);
        String result = null;
        try (CloseableHttpResponse response = httpClient.execute(httpGet);) {
            HttpEntity entity = response.getEntity();
            if (null != entity) {
                result = EntityUtils.toString(entity, ContentType.getOrDefault(entity).getCharset());
                EntityUtils.consume(entity);
            }
        } catch (Exception e) {
            log.error("接口请求异常：{}", e);
        }
        return result;
    }
}

