package api.common.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
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.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * <dependency>
 * <groupId>org.apache.httpcomponents</groupId>
 * <artifactId>httpclient</artifactId>
 * <version>${http.client.version}</version>
 * </dependency>
 */
@Slf4j
public class HttpUtil {

//    public static CloseableHttpClient getHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
//
//        PlainConnectionSocketFactory plainConnectionSocketFactory = new PlainConnectionSocketFactory();
//        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
//                SSLContextBuilder.create().loadTrustMaterial(null, (x509Certificates, s) -> true).build(), // 全部信任 不做身份鉴定
//                new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"},
//                null,
//                NoopHostnameVerifier.INSTANCE);
//        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
//                .register("http", plainConnectionSocketFactory)
//                .register("https", sslConnectionSocketFactory)
//                .build();
//        PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager(registry);
//        pool.setMaxTotal(300);
//        pool.setDefaultMaxPerRoute(300);
//        return HttpClients.custom().setConnectionManager(pool).build();
//    }

    public static CloseableHttpClient getHttpClient() {
        return getCloseableHttpClient();
    }


    private static CloseableHttpClient getCloseableHttpClient() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            X509TrustManager x509TrustManager = new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] arg0, String arg1) {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] arg0, String arg1) {
                }
            };
            sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
//            PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
//            pool.setMaxTotal(300);
//            pool.setDefaultMaxPerRoute(300);
//            return HttpClients.custom().setConnectionManager(pool).setSSLSocketFactory(sslConnectionSocketFactory).build();
            return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
        } catch (Exception exception) {
            throw new RuntimeException("获取 httpClient 失败。", exception);
        }
    }

    public static RequestConfig getRequestConfig() {
        return RequestConfig.custom()
                .setSocketTimeout(24 * 60 * 60 * 1000)
                .setConnectTimeout(24 * 60 * 60 * 1000)
                .setConnectionRequestTimeout(24 * 60 * 60 * 1000)
                .build();
    }

    /**
     * 发送本地文件
     *
     * @param url  http请求路径
     * @param file 文件对象
     * @return 响应体
     */
    public static String post(String url, File file) {
        String result = null;
        try {
            CloseableHttpClient client = getCloseableHttpClient();
            RequestConfig config = getRequestConfig();
            //1 创建 post 请求
            HttpPost post = new HttpPost(url);
            post.setConfig(config);
            //2 设置请求体
            post.setEntity(MultipartEntityBuilder.create().setCharset(StandardCharsets.UTF_8).setMode(HttpMultipartMode.BROWSER_COMPATIBLE)  //设置浏览器兼容模式
                    .addPart("file", new FileBody(file, ContentType.MULTIPART_FORM_DATA)).build());
            //3 获取响应结果
            HttpResponse response = client.execute(post);
            HttpEntity responseEntity = response.getEntity();
            result = EntityUtils.toString(responseEntity);
            //4 关闭流
            EntityUtils.consume(responseEntity);
            return result;
        } catch (org.apache.http.conn.ConnectTimeoutException cte) {
            log.debug(url + " 请求超时。", cte);
            log.info(url + " 请求超时。");
        } catch (Exception e) {
            log.info("请求 " + url + " 失败。", e);
        }
        return result;
    }

    /**
     * 默认请求头的 get 请求，参数拼接到 url 上
     *
     * @param url 请求路径带参数
     * @return 请求结果
     */
    public static String get(String url) {
        log.info("发送HTTP请求：" + url);

        String result;
        CloseableHttpClient closeableHttpClient = null;
        try {
            closeableHttpClient = getCloseableHttpClient();
            //1 创建 GET 对象
            HttpGet get = new HttpGet(url);
            get.setConfig(getRequestConfig());
            //2 执行获取请求结果
            result = EntityUtils.toString(getCloseableHttpClient().execute(get).getEntity());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                closeableHttpClient.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return result;
    }

    /**
     * 默认请求头的 get 请求
     *
     * @param closeableHttpClient http 客户端
     * @param requestConfig       请求配置
     * @param url                 请求路径带参数
     * @return 请求结果
     */
    public static String get(CloseableHttpClient closeableHttpClient, RequestConfig requestConfig, String url) {
        log.info("发送HTTP请求：" + url);
        String result = null;
        try {
            //1 创建 post 请求
            HttpGet get = new HttpGet(url);
            get.setConfig(requestConfig);
            //2 发送请求
            CloseableHttpResponse response = closeableHttpClient.execute(get);
            //3 处理返回结果，如果状态码为200,就是正常返回
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                result = EntityUtils.toString(response.getEntity());
            } else {
                log.error(response.getEntity().toString());
                throw new RuntimeException("请求错误：" + response);
            }

        } catch (org.apache.http.conn.ConnectTimeoutException cte) {
            log.info(url + " 请求超时。", cte);
        } catch (Exception e) {
            log.info("请求 " + url + " 失败。", e);
        }

        return result;
    }

    /**
     * 带请求头的 get 请求
     *
     * @param closeableHttpClient http 客户端
     * @param requestConfig       请求配置
     * @param url                 请求路径带参数
     * @param headers             请求头
     * @return 请求结果
     * @throws IOException IO异常
     */
    public static String get(CloseableHttpClient closeableHttpClient, RequestConfig requestConfig, String url,
                             Map<String, String> headers) throws IOException {
        //1 创建 post 请求
        HttpGet get = new HttpGet(url);
        //2 设置请求默认配置
        get.setConfig(requestConfig);
        //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
        if (!CollectionUtil.isEmpty(headers)) {
            headers.forEach(get::setHeader);
        }
        //4 发送请求
        CloseableHttpResponse response = closeableHttpClient.execute(get);
        //5 处理返回结果，
        //5-1 如果状态码为200,就是正常返回
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            return EntityUtils.toString(response.getEntity());
        } else {
            throw new RuntimeException("------- 请求错误：" + statusCode);
        }
    }

    /**
     * 默认请求头的 get 请求下载文件
     *
     * @param url 请求地址
     * @return 文件流
     */
    public static InputStream getInputStream(String url) {
        try {
            log.info("发送请求：" + url);
            CloseableHttpClient closeableHttpClient = getCloseableHttpClient();
            RequestConfig requestConfig = getRequestConfig();
            return getInputStream(closeableHttpClient, requestConfig, url, null);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 默认请求头的 get 请求下载文件
     *
     * @param url 请求地址
     * @return 文件流
     * @throws IOException 异常
     */
    public static InputStream getInputStream(CloseableHttpClient closeableHttpClient, RequestConfig requestConfig, String url) throws IOException {
        return getInputStream(closeableHttpClient, requestConfig, url, null);
    }

    /**
     * 自定义请求头的 get 请求下载文件
     *
     * @param url     请求地址
     * @param headers 请求头，可为 null
     * @return 文件流
     * @throws IOException 异常
     */
    public static InputStream getInputStream(CloseableHttpClient closeableHttpClient, RequestConfig requestConfig, String url, Map<String, String> headers) throws IOException {
        InputStream result = null;
        try {
            //1 创建 post 请求
            HttpGet get = new HttpGet(url);
            //2 设置请求默认配置
            get.setConfig(requestConfig);
            //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
            if (!CollectionUtil.isEmpty(headers)) {
                headers.forEach(get::setHeader);
            }
            //4 发送请求
            CloseableHttpResponse response = closeableHttpClient.execute(get);
            //5 处理返回结果，
            //5-1 如果状态码为200,就是正常返回
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("请求 " + url + " 状态码为：" + statusCode);
            if (statusCode == 200) {
                result = response.getEntity().getContent();
            }
        } catch (Exception e) {
            log.info("请求 " + url + " 失败。", e);
        }
        return result;
    }

    /**
     * 自定义请求头的 post 请求
     */
    public static String post(String url, Map<String, String> headers, Map<String, String> params) {
        log.info("发送HTTP请求：" + url + "，请求参数：" + params + "，请求头：" + headers);
        try {
            CloseableHttpClient closeableHttpClient = getCloseableHttpClient();
            RequestConfig requestConfig = getRequestConfig();
            //1 创建 post 请求
            HttpPost post = new HttpPost(url);
            //2 设置请求默认配置
            post.setConfig(requestConfig);
            //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
            if (!CollectionUtil.isEmpty(headers)) {
                headers.forEach(post::setHeader);
            }
            //4 设置请求体
            post.addHeader("Content-Type", "application/json");
            if (!CollectionUtil.isEmpty(params)) {
                post.setEntity(new StringEntity(JsonUtil.beanToJson(params), "utf-8"));
            }
            //5 发送请求
            CloseableHttpResponse response = closeableHttpClient.execute(post);
            //6 处理返回结果，
            //6-1 如果状态码为200,就是正常返回
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                return EntityUtils.toString(response.getEntity());
            } else {
                throw new RuntimeException("请求错误：" + statusCode);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 自定义请求头的 post 请求
     */
    public static String post(CloseableHttpClient closeableHttpClient, RequestConfig requestConfig, String url, Map<String, String> headers, Map<String, String> params) throws IOException {
        //1 创建 post 请求
        HttpPost post = new HttpPost(url);
        //2 设置请求默认配置
        post.setConfig(requestConfig);
        //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
        if (!CollectionUtil.isEmpty(headers)) {
            headers.forEach(post::setHeader);
        }
        //4 设置请求体
        post.addHeader("Content-Type", "application/json");
        if (!CollectionUtil.isEmpty(params)) {
            post.setEntity(new StringEntity(JsonUtil.beanToJson(params), "utf-8"));
        }
        //5 发送请求
        CloseableHttpResponse response = closeableHttpClient.execute(post);
        //6 处理返回结果，
        //6-1 如果状态码为200,就是正常返回
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            return EntityUtils.toString(response.getEntity());
        } else {
            throw new RuntimeException("请求错误：" + statusCode);
        }
    }

//
//    /**
//     * 通过 get 请求下载文件
//     * 发送 get 请求，get 请求的参数在 url 里
//     *
//     * @param url     请求地址
//     * @param headers 请求头，可为 null
//     * @return 文件流
//     * @throws IOException 异常
//     */
//    public static InputStream getDownload(String url, Map<String, String> headers) throws IOException {
//        //1 创建 post 请求
//        HttpGet get = new HttpGet(url);
//        //2 设置请求默认配置
//        get.setConfig(requestConfig);
//        //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
//        if (!CollectionUtil.isEmpty(headers)) {
//            headers.forEach(get::setHeader);
//        }
//        //4 发送请求
//        CloseableHttpResponse response = httpClient.execute(get);
//        //5 处理返回结果，
//        //5-1 如果状态码为200,就是正常返回
//        int statusCode = response.getStatusLine().getStatusCode();
//        if (statusCode == 200) {
//            return response.getEntity().getContent();
//        } else {
//            throw new RuntimeException("------- 请求错误：" + statusCode);
//        }
//    }
//
//
//    /**
//     * 发送 post 请求
//     */
//    public static InputStream postUpload(String url) throws IOException {
//        //1 创建 post 请求
//        HttpPost post = new HttpPost(url);
//        //2 设置请求默认配置
//        post.setConfig(requestConfig);
//        //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
//        post.setHeader("Content-type", "multipart/form-data");
//        //5 发送请求
//        CloseableHttpResponse response = httpClient.execute(post);
//        //6 处理返回结果，
//        //6-1 如果状态码为 200,就是正常返回
//        int statusCode = response.getStatusLine().getStatusCode();
//        if (statusCode == 200) {
//            return response.getEntity().getContent();
//        } else {
//            throw new RuntimeException("------- 请求错误：" + statusCode);
//        }
//    }
//
//    /**
//     * 发送 post 请求
//     */
//    public static InputStream postDownload(String url, Map<String, String> headers, Map<String, String> params) throws IOException {
//        //1 创建 post 请求
//        HttpPost post = new HttpPost(url);
//        //2 设置请求默认配置
//        post.setConfig(requestConfig);
//        //3 设置请求头 post.setHeader("Content-type", "application/json; charset=utf-8");
//        if (!CollectionUtil.isEmpty(headers)) {
//            headers.forEach(post::setHeader);
//        }
//        //4 设置请求体
//        if (!CollectionUtil.isEmpty(params)) {
//            params.forEach(post::setHeader);
//        }
//        //5 发送请求
//        CloseableHttpResponse response = httpClient.execute(post);
//        //6 处理返回结果，
//        //6-1 如果状态码为 200,就是正常返回
//        int statusCode = response.getStatusLine().getStatusCode();
//        if (statusCode == 200) {
//            return response.getEntity().getContent();
//        } else {
//            throw new RuntimeException("------- 请求错误：" + statusCode);
//        }
//    }

}
