package com.tzy.common.utils.http;

import com.alibaba.fastjson.JSONObject;
import com.tzy.common.exception.ServiceException;
import com.tzy.common.utils.MyX509TrustManager;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Pattern;

/**
 * httpclient
 */
@Data
@Slf4j
public class HttpClientUtil {

    public static String POST = "POST";
    public static String GET = "GET";
    private String contentType;
    private String authorization;
    private String url;//请求地址
    private HttpMethod method;//请求方法
    private Map<String, String> header;//请求头
    private Object response;//请求参数
    private String getParam;//get请求参数[处理后]
    private String postParam;//post请求参数[处理后]
    //^http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+(?::\\d+)
    private static final String GET_REQUEST_PATTERN =
            "(?::\\d+)?(/?(?:[a-zA-Z0-9_$-]|[.+&#126;%/:=?&]|(?:%[0-9a-fA-F][0-9a-fA-F]))*)?"
                    + "(?:\\?(?:[a-zA-Z0-9]+(?:=[a-zA-Z0-9-]+)?(?:&[a-zA-Z0-9]+(?:=[a-zA-Z0-9-]+)?)*))?"
                    + "(?:#(?:[a-zA-Z0-9_$-]|[.+&#126;%/:=?&]|(?:%[0-9a-fA-F][0-9a-fA-F]))*)?$";

    public HttpClientUtil(){}

    public HttpClientUtil(String contentType, String authorization){
        this.contentType = contentType;
        this.authorization = authorization;
    }

    /**
     *
     * @param httpUrl  请求的url
     * @param param  form表单的参数（key,value形式）
     * @return
     */
    @SuppressWarnings("resource")
    public String doPostForm(String httpUrl, Map param) {
        HttpsURLConnection connection = null;
        InputStream is = null;
        OutputStream os = null;
        BufferedReader br = null;
        String result = null;
        try {
            log.info("请求地址》》》{}",httpUrl);
            String linkString = createLinkString(param);
            log.info("请求参数》》》{}",linkString);
            //创建SSLContext
            SSLContext sslContext=SSLContext.getInstance("SSL");
            TrustManager[] tm={new MyX509TrustManager()};
            //初始化
            sslContext.init(null, tm, new java.security.SecureRandom());;
            //获取SSLSocketFactory对象
            SSLSocketFactory ssf=sslContext.getSocketFactory();
            URL url = new URL(httpUrl);
            // 通过远程url连接对象打开连接
            connection = (HttpsURLConnection) url.openConnection();
            // 设置连接请求方式
            connection.setRequestMethod("POST");
            // 设置连接主机服务器超时时间：15000毫秒
            connection.setConnectTimeout(150000);
            // 设置读取主机服务器返回数据超时时间：60000毫秒
            connection.setReadTimeout(600000);
            // 默认值为：false，当向远程服务器传送数据/写数据时，需要设置为true
            connection.setDoOutput(true);
            // 默认值为：true，当前向远程服务读取数据时，设置为true，该参数可有可无
            connection.setDoInput(true);
            // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
            connection.setRequestProperty("Content-Type", contentType);
            // 设置鉴权信息：Authorization:
            connection.setRequestProperty("Authorization", authorization);
            //设置当前实例使用的SSLSoctetFactory
            connection.setSSLSocketFactory(ssf);
            // 通过连接对象获取一个输出流
            os = connection.getOutputStream();
            // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的(form表单形式的参数实质也是key,value值的拼接，类似于get请求参数的拼接)
            os.write(linkString.getBytes("utf-8"));
            // 通过连接对象获取一个输入流，向远程读取
            connection.setRequestMethod("POST");
            log.info("链接状态：》》{}", connection.getResponseCode());
            if (connection.getResponseCode() == 200) {
                is = connection.getInputStream();
                // 对输入流对象进行包装:charset根据工作项目组的要求来设置
                br = new BufferedReader(new InputStreamReader(is, "utf-8"));
                StringBuffer sbf = new StringBuffer();
                String temp = null;
                // 循环遍历一行一行读取数据
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // 断开与远程地址url的连接
            connection.disconnect();
        }
        return result;
    }

    /**
     *
     * @param url
     * @param map
     * @return
     */
    public String doPostFormV2(String url,Map<String,String> map) {
        List<NameValuePair> urlParameters = new ArrayList<>();
        if(null != map && !map.isEmpty()){
            Set set = map.keySet();
            Iterator<String> iterator = set.iterator();
            while (iterator.hasNext()){
                String key = iterator.next();
                urlParameters.add(new BasicNameValuePair(key,map.get(key)));
            }
        }
        CloseableHttpClient httpclient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        HttpPost post = new HttpPost(url);
        // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
        post.addHeader("Content-Type", contentType);
        // 设置鉴权信息：Authorization:
        post.addHeader("Authorization", authorization);
        try {
            post.setEntity( new UrlEncodedFormEntity(urlParameters, HTTP.UTF_8));
            try {
                response = httpclient.execute(post); // 判断网络连接状态码是否正常(0--200都数正常)
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
                    String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                    log.info("=============={}",content);
                    return content;
                }
                EntityUtils.consume(response.getEntity());
            } catch (IOException e) {
                e.printStackTrace();
            }  finally {
                try {
                    if (null != response) {
                        response.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } finally {
            //释放链接
            try {
                httpclient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /*
     * 处理https GET/POST请求
     * @param requestUrl 请求地址、
     * @param method 请求方法[GET/POST]
     * @param param 参数
     * @return 返回值
     * */
    public String httpsPostRequest(String requestUrl,JSONObject jsonObject ){
        StringBuffer buffer=null;
        try{
            log.info(" 请求地址》》》{} ",requestUrl);
            log.info(" 请求参数》》》{} ",jsonObject);
            //创建SSLContext
            SSLContext sslContext=SSLContext.getInstance("SSL");
            TrustManager[] tm={new MyX509TrustManager()};
            //初始化
            sslContext.init(null, tm, new java.security.SecureRandom());
            //获取SSLSocketFactory对象
            SSLSocketFactory ssf=sslContext.getSocketFactory();
            URL url=new URL(requestUrl);
            HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
            conn.setRequestProperty("Content-Type", contentType);
            // 设置鉴权信息：Authorization:
            conn.setRequestProperty("Authorization", authorization);
            //设置当前实例使用的SSLSoctetFactory
            conn.setSSLSocketFactory(ssf);
            conn.connect();
            //往服务器端写内容
            if(null!=jsonObject){
                OutputStream os=conn.getOutputStream();
                os.write(JSONObject.toJSONString(jsonObject).getBytes("utf-8"));
                os.close();
            }
            //读取服务器端返回的内容
            InputStream is=conn.getInputStream();
            InputStreamReader isr=new InputStreamReader(is,"utf-8");
            BufferedReader br=new BufferedReader(isr);
            buffer=new StringBuffer();
            String line=null;
            while((line=br.readLine())!=null){
                buffer.append(line);
            }
            return buffer.toString();
        }catch(Exception e){
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
    public String httpsGetRequest(String requestUrl){
        StringBuffer buffer=null;
        try{
            log.info(" 请求地址》》》{} ",requestUrl);
            //创建SSLContext
            SSLContext sslContext=SSLContext.getInstance("SSL");
            TrustManager[] tm={new MyX509TrustManager()};
            //初始化
            sslContext.init(null, tm, new java.security.SecureRandom());
            //获取SSLSocketFactory对象
            SSLSocketFactory ssf=sslContext.getSocketFactory();
            URL url=new URL(requestUrl);
            HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-Type", contentType);
            // 设置鉴权信息：Authorization:
            conn.setRequestProperty("Authorization", authorization);
            //设置当前实例使用的SSLSoctetFactory
            conn.setSSLSocketFactory(ssf);
            conn.connect();
            //读取服务器端返回的内容
            InputStream is=conn.getInputStream();
            InputStreamReader isr=new InputStreamReader(is,"utf-8");
            BufferedReader br=new BufferedReader(isr);
            buffer=new StringBuffer();
            String line=null;
            while((line=br.readLine())!=null){
                buffer.append(line);
            }
            return buffer.toString();
        }catch(Exception e){
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /*
     * 处理https GET/POST请求
     * @param requestUrl 请求地址、
     * @param method 请求方法[GET/POST]
     * @param param 参数
     * @return 返回值
     * */
    public static String httpsRequest(String requestUrl,String method,String param , Map<String ,String> header){
        StringBuffer buffer=null;
        try{
            //创建SSLContext
            SSLContext sslContext=SSLContext.getInstance("SSL");
            TrustManager[] tm={new MyX509TrustManager()};
            //初始化
            sslContext.init(null, tm, new java.security.SecureRandom());
            //获取SSLSocketFactory对象
            SSLSocketFactory ssf=sslContext.getSocketFactory();
            URL url=new URL(requestUrl);
            HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod(method);
            if(null != header && !header.isEmpty()){
                Set set = header.keySet();
                Iterator<String> iterator = set.iterator();
                while (iterator.hasNext()){
                    String key = iterator.next();
                    conn.setRequestProperty(key, header.get(key));
                }
            }
            //设置当前实例使用的SSLSoctetFactory
            conn.setSSLSocketFactory(ssf);
            conn.connect();
            //往服务器端写内容
            if(null!=param){
                OutputStream os=conn.getOutputStream();
                os.write(param.getBytes("utf-8"));
                os.close();
            }
            //读取服务器端返回的内容
            InputStream is=conn.getInputStream();
            InputStreamReader isr=new InputStreamReader(is,"utf-8");
            BufferedReader br=new BufferedReader(isr);
            buffer=new StringBuffer();
            String line=null;
            while((line=br.readLine())!=null){
                buffer.append(line);
            }
            return buffer.toString();
        }catch(Exception e){
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    /**
     * http 请求
     * @param requestUrl
     * @param method
     * @param param
     * @param header
     * @return
     */
    public static String httpRequest(String requestUrl,String method,String param , Map<String ,String> header){
        StringBuffer buffer=null;
        try{
            TrustManager[] tm={new MyX509TrustManager()};
            //初始化
            URL url=new URL(requestUrl);
            sun.net.www.protocol.http.HttpURLConnection conn = (sun.net.www.protocol.http.HttpURLConnection) url.openConnection();
//            HttpsURLConnection conn=(HttpsURLConnection)url.openConnection();
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod(method);
            if(null != header && !header.isEmpty()){
                Set set = header.keySet();
                Iterator<String> iterator = set.iterator();
                while (iterator.hasNext()){
                    String key = iterator.next();
                    conn.setRequestProperty(key, header.get(key));
                }
            }
            conn.connect();
            //往服务器端写内容
            if(null!=param){
                OutputStream os=conn.getOutputStream();
                os.write(param.getBytes("utf-8"));
                os.close();
            }
            //读取服务器端返回的内容
            InputStream is=conn.getInputStream();
            InputStreamReader isr=new InputStreamReader(is,"utf-8");
            BufferedReader br=new BufferedReader(isr);
            buffer=new StringBuffer();
            String line=null;
            while((line=br.readLine())!=null){
                buffer.append(line);
            }
            return buffer.toString();
        }catch(Exception e){
            log.error(e.getMessage());
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public static String createLinkString(Map<String, String> params) {

        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        StringBuilder prestr = new StringBuilder();
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
                prestr.append(key).append("=").append(value);
            } else {
                prestr.append(key).append("=").append(value).append("&");
            }
        }
        return prestr.toString();
    }

    /**
     *
     * @param param
     * @return
     */
    private boolean isGetParam(String param){
        Pattern pattern = Pattern.compile(GET_REQUEST_PATTERN);
        return pattern.matcher(param).matches();
    }
    /**
     * @return
     */
    public static HttpClientUtil create(){
        return new HttpClientUtil();
    }

    /**
     * @param url
     * @return
     */
    public HttpClientUtil uri(String url,HttpMethod method){
        this.url = url;
        this.method = method;
        return this;
    }

    /**
     * @param response
     * @return
     */
    public HttpClientUtil entity(Object response){
        this.response = response;
        return this;
    }

    /**
     * @param header
     * @return
     */
    public HttpClientUtil header(Map<String,String> header){
        this.header = header;
        return this;
    }

    /**
     * @return
     */
    public HttpClientUtil build() throws ServiceException {
        //处理请求参数
        if(Objects.isNull(this.method)) throw new ServiceException("请设置请求方法");
        switch (this.method){
            case GET: //如果是GET请求
                //处理请求参数
                if(!Objects.isNull(this.response)){
                    //如果不是字符串
                    if(!(Objects.equals("string", StringUtils.lowerCase(this.response.getClass().getName()))
                            && isGetParam(String.valueOf(this.response)))
                    ){
                        try{
                            //默认为对象类型
                            JSONObject paramJson = JSONObject.parseObject(JSONObject.toJSONString(this.response));
                            StringBuilder preStr = new StringBuilder();
                            List<String> keys = new ArrayList<>(paramJson.keySet());
                            Collections.sort(keys);
                            keys.forEach(key-> preStr.append(key).append("=").append(paramJson.get(key)).append("&"));
                            if(String.valueOf(preStr).endsWith("&") && String.valueOf(preStr).length() > 1){
                                this.getParam = String.valueOf(preStr).substring(0,String.valueOf(preStr).length()-1);
                            }
                        }catch (Exception e){
                            log.error(" 处理get请求参数时异常 ",e);
                            throw new ServiceException("处理get请求参数时异常");
                        }
                    }else{
                        this.getParam = String.valueOf(this.response);
                    }
                }
                break;
            case POST:
                //请求参数 设置为 json
                this.postParam = JSONObject.toJSONString(this.response);
                /*if(Objects.equals("json",StringUtils.lowerCase(this.response.getClass().getName()))){

                }else{
                }*/
                break;
        }
        return this;
    }

    /**
     * @return
     * @throws Exception
     */
    public String execute() throws ServiceException{
        if(StringUtils.isBlank(this.url)) throw new ServiceException("请设置请求地址");
        if(Objects.isNull(this.method)) throw new ServiceException("请设置请求方法");
        String ret = "";
        if(this.url.startsWith("https")){
            ret = httpsRequest(url, this.method.getCode(), this.postParam, this.header);
        }else{
            ret = httpRequest(url, this.method.getCode(), this.postParam, this.header);
        }
        return ret;
    }
}
