package com.fzm.mall.server.admin.util;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 目前JAVA实现HTTP请求的方法用的最多的有两种：
 * 1.通过HttpURLConnection去实现
 * java.net.HttpURLConnection是JAVA的标准类，是JAVA比较原生的一种实现方式。
 * 2.一种是通过HTTPClient这种第三方的开源框架去实现。
 * HttpClient3.1是org.apache.commons.httpclient下操作远程url的工具包，
 * HttpClient4.5是org.apache.http.client下操作远程url的工具包，最新的；
 *
 * @author wangpeng
 * @version 1.0
 * @description apacheHttpClient4
 * @create_time 2020/7/16 0016 17:04:32
 */
@Slf4j
public class HttpUtils {

    private static CloseableHttpClient httpClient = null;
    private static CloseableHttpClient httpsClient = null;
    private static RequestConfig requestConfig = null;

    //初始化一个httpClient实例（公用）设置请求相关配置
    static {
        httpClient = HttpClients.createDefault();
        requestConfig = RequestConfig.custom()
                //设置连接超时
                .setConnectTimeout(35000)
                //设置从连接池获取连接实例的超时
                .setConnectionRequestTimeout(35000)
                //设置读取超时
                .setSocketTimeout(60000)
                .build();
        //忽略证书的
        httpsClient = createSSLInsecureClient();
        //带有证书的ssl请求createSSLCertInsecureClient(certPath, certPassword);
        //httpsClient=createSSLCertInsecureClient();
    }

    /**
     * 发送httpGet请求
     *
     * @param httpUrl     httpGet请求地址
     * @param paramsJson  httpGet请求参数
     * @param headersJson httpGet请求头
     * @return 请求地址服务器返回的数据
     */
    public static String apacheHttpClient4DoGet(
            String httpUrl, JSONObject paramsJson, JSONObject headersJson) {
        String result = null;
        HttpGet httpGet = new HttpGet(setApacheHttpClient4GetUrl(httpUrl, paramsJson));
        setApacheHttpClient4GetRequestHeader(httpGet, headersJson);
        httpGet.setConfig(requestConfig);
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 发送httpsGet请求
     *
     * @param httpUrl     httpGet请求地址
     * @param paramsJson  httpGet请求参数
     * @param headersJson httpGet请求头
     * @return 请求地址服务器返回的数据
     */
    public static String apacheHttpsClient4DoGet(
            String httpUrl, JSONObject paramsJson, JSONObject headersJson) {
        String result = null;
        HttpGet httpGet = new HttpGet(setApacheHttpClient4GetUrl(httpUrl, paramsJson));
        setApacheHttpClient4GetRequestHeader(httpGet, headersJson);
        httpGet.setConfig(requestConfig);
        try (CloseableHttpResponse response = httpsClient.execute(httpGet)) {
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 发送httpPost请求
     *
     * @param httpUrl     httpPost请求地址
     * @param paramsJson  httpPost请求参数
     * @param headersJson httpPost请求头
     * @return 请求地址服务器返回的数据
     */
    public static String apacheHttpClient4DoPost(
            String httpUrl, JSONObject paramsJson, JSONObject headersJson) {
        String result = null;
        HttpPost httpPost = new HttpPost(httpUrl);
        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
        setApacheHttpClient4PostRequestHeader(httpPost, headersJson);
        httpPost.setEntity(new StringEntity(paramsJson.toJSONString(), StandardCharsets.UTF_8));
//        setApacheHttpClient4PostPrams(httpPost, paramsJson);
        httpPost.setConfig(requestConfig);
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 发送httpsPost请求
     *
     * @param httpUrl     httpPost请求地址
     * @param paramsJson  httpPost请求参数
     * @param headersJson httpPost请求头
     * @return 请求地址服务器返回的数据
     */
    public static String apacheHttpsClient4DoPost(
            String httpUrl, JSONObject paramsJson, JSONObject headersJson) {
        String result = null;
        HttpPost httpPost = new HttpPost(httpUrl);
        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
        setApacheHttpClient4PostRequestHeader(httpPost, headersJson);
        httpPost.setEntity(new StringEntity(paramsJson.toJSONString(), StandardCharsets.UTF_8));
//        setApacheHttpClient4PostPrams(httpPost, paramsJson);
        httpPost.setConfig(requestConfig);
        try (CloseableHttpResponse response = httpsClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 拼接httpGet请求地址
     *
     * @param httpUrl    httpGet请求地址
     * @param paramsJson httpGet请求参数
     * @return 拼接后的地址
     */
    private static String setApacheHttpClient4GetUrl(String httpUrl, JSONObject paramsJson) {
        if (paramsJson != null && paramsJson.size() > 0) {
            httpUrl += paramsJson.keySet().stream()
                    .map(key -> key + "=" + paramsJson.get(key) + "&")
                    .reduce("?", (x, y) -> x + y);
            httpUrl = httpUrl.substring(0, httpUrl.length() - 1);
        }
        return httpUrl;
    }

    /**
     * 封装httpPost请求参数
     *
     * @param httpPost   httpPost请求地址
     * @param paramsJson httpPost请求参数
     */
    private static void setApacheHttpClient4PostPrams(HttpPost httpPost, JSONObject paramsJson) {
        try {
            if (paramsJson != null && paramsJson.size() > 0) {
                List<NameValuePair> nvpList = paramsJson
                        .keySet()
                        .stream()
                        .map(key -> new BasicNameValuePair(key, paramsJson.getString(key)))
                        .collect(Collectors.toList());
                httpPost.setEntity(new UrlEncodedFormEntity(nvpList, "UTF-8"));
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置httpGet请求头
     *
     * @param httpGet     {@link HttpGet}
     * @param headersJson httpGet请求头
     */
    private static void setApacheHttpClient4GetRequestHeader(HttpGet httpGet, JSONObject headersJson) {
        if (headersJson != null && headersJson.size() > 0) {
            headersJson.keySet().forEach(key -> httpGet.addHeader(key, headersJson.getString(key)));
        }
    }

    /**
     * 设置httpPost请求头
     *
     * @param httpPost    {@link HttpPost}
     * @param headersJson httpPost请求头
     */
    private static void setApacheHttpClient4PostRequestHeader(HttpPost httpPost, JSONObject headersJson) {
        if (headersJson != null && headersJson.size() > 0) {
            headersJson.keySet().forEach(key -> httpPost.addHeader(key, headersJson.getString(key)));
        }
    }

    /**
     * httpsClient证书初始化
     *
     * @param certPath     证书路径
     * @param certPassword 证书密码
     */
    @SuppressWarnings("unused")
    private static CloseableHttpClient createSSLCertInsecureClient(String certPath, String certPassword) {
        SSLContext sslcontext = null;
        try {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            FileInputStream inputStream = new FileInputStream(new File(certPath));
            keyStore.load(inputStream, certPassword.toCharArray());
            sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, certPassword.toCharArray()).build();
        } catch (KeyStoreException | NoSuchAlgorithmException | IOException | CertificateException
                | KeyManagementException | UnrecoverableKeyException e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory sslCsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"},
                null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        return HttpClients.custom().setSSLSocketFactory(sslCsf).build();
    }

    /**
     * 创建一个SSL信任所有证书的httpClient对象
     * 信任任意证书 (简单，但不建议用于生产代码)
     *
     * @return httpsClient对象
     */
    @SuppressWarnings("deprecation")
    public static CloseableHttpClient createSSLInsecureClient() {
        SSLContext sslContext = null;
        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                    return true;
                }
            }).build();
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            e.printStackTrace();
        }
        assert sslContext != null;
        SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslContext,
                SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        return HttpClients.custom().setSSLSocketFactory(factory).build();
    }

    public static String getRealIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

}
