package com.cmeeting.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
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.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.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
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.util.EntityUtils;
import org.springframework.web.multipart.MultipartFile;

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


/**
 * @author liuzhenmeng
 * @date 2024/6/27 19:48
 */
public class HttpClientKnowledgePlatformUtil {

    private static PoolingHttpClientConnectionManager connPoolMng;
    private static RequestConfig requestConfig;
    private static int maxTotal = 500;
    private static final Log log = LogFactory.get();

    /**
     * 私有构造方法
     * 单例中连接池初始化一次
     */
    private HttpClientKnowledgePlatformUtil(){
        //初始化http连接池
        SSLContext sslContext = getSSLContext();
        Registry<ConnectionSocketFactory> socketFactoryRegistry =
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.INSTANCE)
                        .register("https", new SSLConnectionSocketFactory(sslContext)).build();
        connPoolMng = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connPoolMng.setMaxTotal(maxTotal);
        connPoolMng.setDefaultMaxPerRoute(maxTotal/5);
        //初始化请求超时控制参数
        requestConfig = RequestConfig.custom()
                .setConnectTimeout(1000) //连接超时时间
                .setConnectionRequestTimeout(5000) //从线程池中获取线程超时时间
                .setSocketTimeout(1000) //设置数据超时时间
                .build();
    }


    /**
     * 获取client客户端
     * @return
     */
    public static CloseableHttpClient getClient() {
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connPoolMng)
                .setDefaultRequestConfig(requestConfig)
                .build();
        return httpClient;
    }



    /**
     * 发送get请求
     *
     * @param url 路径
     * @return
     */
    public static String sendHttpGet(String url) {
        log.info("请求地址：【{}】", url);
        log.info("请求方式：GET");
        // get请求返回结果
        // 发送get请求
        HttpGet request = new HttpGet(url);
        request.setConfig(requestConfig);
        CloseableHttpResponse response = null;
        String result = "";
        try {
            response = getClient().execute(request);
            // 请求发送成功，并得到响应
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            log.info("请求返回结果：【{}】", result);
        } catch (IOException e) {
            log.error("get请求提交失败:" + url, e);
        } finally {
            if (response != null){
                try {
                    //此处调优重点，多线程模式下可提高性能。
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (IOException e) {
                    System.out.println("关闭response失败:" + e);
                }
            }
            request.releaseConnection();
        }
        return result;
    }



    /**
     * 发送POST请求
     *
     * @param url
     * @param parametersBody
     * @return
     */
    public static String sendPostJsonRequest(String url, String parametersBody) {
        log.info("请求地址：【{}】", url);
        log.info("请求方式：POST");
        log.info("请求参数：【{}】", parametersBody);
        HttpPost post = new HttpPost(url);
        post.addHeader("Content-Type", "application/json;charset=UTF-8");
        post.addHeader("Accept", "application/json");
        post.setConfig(requestConfig);
        StringEntity entity = new StringEntity(parametersBody, "UTF-8");
        post.setEntity(entity);

        //设置请求头
//        if (StringUtils.isNotBlank(token)){
//            post.setHeader("Authorization", token);
//        }
        String result = "";
        CloseableHttpResponse response = null;
        try {
            response = getClient().execute(post);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            log.info("请求返回结果：【{}】", result);
            return result;
        } catch (ClientProtocolException e) {
            throw new RuntimeException("postRequest -- Client protocol exception!", e);
        } catch (IOException e) {
            throw new RuntimeException("postRequest -- IO error!", e);
        } finally {
            if (response != null){
                try {
                    //此处调优重点，多线程模式下可提高性能。
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (IOException e) {
                    log.error("关闭response失败:" + e);
                }
            }
            post.releaseConnection();
        }
    }

    /**
     * 发送POST请求
     *
     * @param url
     * @param parametersBody
     * @return
     */
    public static String sendPostJsonBySearch(String url, String parametersBody) {
        log.info("请求地址：【{}】", url);
        log.info("请求方式：POST");
        log.info("请求参数：【{}】", parametersBody);
        HttpPost post = new HttpPost(url);
        post.addHeader("Content-Type", "application/json;charset=UTF-8");
        post.addHeader("Accept", "application/json");
        post.setConfig(requestConfig);
        StringEntity entity = new StringEntity(parametersBody, "UTF-8");
        post.setEntity(entity);
        String result = "";
        CloseableHttpResponse response = null;
        try {
            response = getClient().execute(post);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            return result;
        } catch (ClientProtocolException e) {
            throw new RuntimeException("postRequest -- Client protocol exception!", e);
        } catch (IOException e) {
            throw new RuntimeException("postRequest -- IO error!", e);
        } finally {
            if (response != null){
                try {
                    //此处调优重点，多线程模式下可提高性能。
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (IOException e) {
                    log.error("关闭response失败:" + e);
                }
            }
            post.releaseConnection();
        }
    }

    /**
     * 使用httpclint 发送文件，如果不传输文件，直接设置fileParams=null，
     * 如果不设置请求头参数，直接设置headerParams=null，就可以进行普通参数的POST请求了
     *
     * @param url          请求路径
     * @param file   文件参数
     * @param otherParams  其他字符串参数
     * @return
     */
    public static String sendPostByFormData(String url, MultipartFile file, Map<String, String> otherParams) {
        log.info("请求地址：{}", url);
        log.info("请求方式：POST");
        log.info("请求参数：{}", JSON.toJSONString(otherParams));
        String result = "";
        CloseableHttpResponse response = null;
        HttpPost httpPost = new HttpPost(url);
        try {
            httpPost.setConfig(requestConfig);
            //设置请求头
//            if (headerParams != null && headerParams.size() > 0) {
//                for (Map.Entry<String, String> e : headerParams.entrySet()) {
//                    String value = e.getValue();
//                    String key = e.getKey();
//                    if (StringUtils.isNotBlank(value)) {
//                        httpPost.setHeader(key, value);
//                    }
//                }
//            }
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(StandardCharsets.UTF_8);
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            ContentType contentType = ContentType.create("multipart/form-data", StandardCharsets.UTF_8);
            //    文件传输http请求头(multipart/form-data)
            if (file != null) {
                builder.addBinaryBody("file", file.getBytes(), ContentType.MULTIPART_FORM_DATA, file.getOriginalFilename());
            }
            //    字节传输http请求头
            if (otherParams != null && otherParams.size() > 0) {
                for (Map.Entry<String, String> e : otherParams.entrySet()) {
                    String value = e.getValue();
                    if (StringUtils.isNotBlank(value)) {
                        builder.addTextBody(e.getKey(), value, contentType);// 类似浏览器表单提交，对应input的name和value
                    }
                }
            }
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            response = getClient().execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            log.info("响应数据：{}", JSON.toJSONString(result));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null){
                try {
                    //此处调优重点，多线程模式下可提高性能。
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (IOException e) {
                    log.error("关闭response失败:" + e);
                }
            }
            httpPost.releaseConnection();
        }
        return result;
    }

    /**
     * 使用httpclint 发送文件，如果不传输文件，直接设置fileParams=null，
     * 如果不设置请求头参数，直接设置headerParams=null，就可以进行普通参数的POST请求了
     *
     * @param url          请求路径
     * @param fileList   文件参数
     * @param otherParams  其他字符串参数
     * @return
     */
    public static String sendPostByFormDataFiles(String url, List<MultipartFile> fileList, Map<String, String> otherParams, String fileKey) {
        log.info("请求地址：{}", url);
        log.info("请求方式：POST");
        log.info("请求参数：{}", JSON.toJSONString(otherParams));

        String result = "";
        CloseableHttpResponse response = null;
        HttpPost httpPost = new HttpPost(url);
        try {
            httpPost.setConfig(requestConfig);
            //设置请求头
//            if (headerParams != null && headerParams.size() > 0) {
//                for (Map.Entry<String, String> e : headerParams.entrySet()) {
//                    String value = e.getValue();
//                    String key = e.getKey();
//                    if (StringUtils.isNotBlank(value)) {
//                        httpPost.setHeader(key, value);
//                    }
//                }
//            }
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(StandardCharsets.UTF_8);
            builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
            ContentType contentType = ContentType.create("multipart/form-data",Charset.forName("UTF-8"));
            //    文件传输http请求头(multipart/form-data)
            String key = fileKey==null?"files":fileKey;
            if (CollectionUtil.isNotEmpty(fileList)) {
                for (MultipartFile file:fileList){
                    builder.addBinaryBody(key, file.getBytes(), ContentType.MULTIPART_FORM_DATA, file.getOriginalFilename());
                }
            }
            //    字节传输http请求头
            if (otherParams != null && otherParams.size() > 0) {
                for (Map.Entry<String, String> e : otherParams.entrySet()) {
                    String value = e.getValue();
                    if (StringUtils.isNotBlank(value)) {
                        builder.addTextBody(e.getKey(), value, contentType);// 类似浏览器表单提交，对应input的name和value
                    }
                }
            }
            HttpEntity entity = builder.build();
            httpPost.setEntity(entity);
            response = getClient().execute(httpPost);
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                // 将响应内容转换为字符串
                result = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
            }
            log.info("响应数据：{}", JSON.toJSONString(result));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (response != null){
                try {
                    //此处调优重点，多线程模式下可提高性能。
                    EntityUtils.consume(response.getEntity());
                    response.close();
                } catch (IOException e) {
                    log.error("关闭response失败:" + e);
                }
            }
            httpPost.releaseConnection();
        }
        return result;
    }


    private static SSLContext getSSLContext() {
        try {
            // 这里可以填两种值 TLS和LLS , 具体差别可以自行搜索
            SSLContext sc = SSLContext.getInstance("TLS");
            // 构建新对象
            X509TrustManager manager = new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                }

                // 这里返回Null
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            sc.init(null, new TrustManager[]{manager}, null);
            return sc;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


}
