package com.quantgroup.asset.distribution.service.httpclient.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Predicates;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.quantgroup.asset.distribution.service.httpclient.IHttpService;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
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.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.util.*;

/**
 * 
 *
 * @author renfeng
 * @date: 2017年5月8日 下午12:24:15
 * @version v1.0
 */
@Service
public class HttpServiceImpl implements IHttpService {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpServiceImpl.class);

    private static final int TIME_OUT_60 = 60 * 1000;  
    private static final int TIME_OUT_20 = 30 * 1000;

    private static Map<String, String> defaultJsonHeaders;

    @Autowired
    @Qualifier("httpClient")
    private CloseableHttpClient httpClient;
    @Autowired
    @Qualifier("niwodaiHttpsClient")
    private CloseableHttpClient niwodaiHttpsClient;

    static {
        defaultJsonHeaders = new HashMap<>();
        defaultJsonHeaders.put("Content-Type", "application/json");
    }

    @Override
    public String get(String uri) {
        return get(uri, null, null);
    }
    @Override
    public String get(String uri,int timeOut) {
        return doHttp(RequestBuilder.get(), uri, null, null,true,timeOut);
    }

    @Override
    public <T> T get(String uri, Class<T> classOfT) {
        String s = get(uri, null, null);
        return new Gson().fromJson(s,classOfT);
    }

    @Override
    public String get(String uri, Map<String, String> parameters) {
        return get(uri, null, parameters);
    }

    @Override
    public String get(String uri, Map<String, String> headers, Map<String, String> parameters) {
        return doHttp(RequestBuilder.get(), uri, headers, parameters,true,TIME_OUT_60);
    }

    @Override
    public String post(String uri) {
        return post(uri, null, null);
    }

    @Override
    public String post(String uri, Map<String, String> parameters) {
        return post(uri, null, parameters);
    }

    @Override
    public String post(String uri, Map<String, String> headers,Map<String, String> parameters,boolean isLog) {
        return doHttp(RequestBuilder.post(), uri, headers, parameters,isLog,TIME_OUT_60);
    }

    @Override
    public String post(String uri, Map<String, String> headers,Map<String, String> parameters,boolean isLog,int timeOut) {
        return doHttp(RequestBuilder.post(), uri, headers, parameters,isLog,timeOut);
    }

    @Override
    public String post(String uri, Map<String, String> headers, Map<String, String> parameters) {
        return doHttp(RequestBuilder.post(), uri, headers, parameters,true,TIME_OUT_60);
    }
    @Override
   	public String postJson(String uri, Map<String, String> headers, String jsonParams) {
   		return doJsonHttp(RequestBuilder.post(),uri,headers,jsonParams);
   	}

    @Override
    public String postJson(String uri, String jsonParams) {
        return postJson(uri, defaultJsonHeaders, jsonParams);
    }

    @Override
    public String postJson(String uri, Map<String, String> params) {
        return postJson(uri, defaultJsonHeaders, JSON.toJSONString(params));
    }

    @Override
    public String postObj(String uri, Map<String, Object> parameters) {
        return postObj(uri, null, parameters);
    }
    @Override
   	public String postObj(String uri, Map<String, String> headers, Map<String, Object> parameters) {
   		return doObjectHttp(RequestBuilder.post(),uri,headers,parameters);
   	}
    @Override
    public int postNoResponse(String url, Map<String, String> parameters){
    	return doHttpNoResponse(RequestBuilder.post(),url,null,parameters);
    }

    @Override
    public Map<String,String> postHasResponse(String url, Map<String, String> parameters){
        return doHttpHasResponse(RequestBuilder.post(),url,null,parameters);
    }
    
    private int doHttpNoResponse(RequestBuilder requestBuilder,String uri, Map<String, String> headers, Map<String, String> parameters){
    	HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,TIME_OUT_20);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, parameters);
            httpResponse = httpClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if(statusCode==200){
            	 httpEntity = httpResponse.getEntity();
            }else{
            	 LOGGER.info("HTTP请求失败, uri:{},parameters:{},响应码:{}", uri,parameters,statusCode);
            }
            return statusCode;
        } catch (Exception e) {
            LOGGER.error("HTTP请求异常, uri:{},parameters:{}", uri,parameters);
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
        return 808;
    }


    private Map<String,String> doHttpHasResponse(RequestBuilder requestBuilder,String uri, Map<String, String> headers, Map<String, String> parameters){
        Map<String,String> responseMap = new HashMap<>();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,TIME_OUT_20);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, parameters);
            httpResponse = httpClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            if(statusCode==200){
                httpEntity = httpResponse.getEntity();
                responseMap.put("response",EntityUtils.toString(httpEntity, "UTF-8"));
            }else{
                LOGGER.info("HTTP请求失败, uri:{},parameters:{},响应码:{}", uri,parameters,statusCode);
            }
            responseMap.put("statusCode",String.valueOf(statusCode));
            return responseMap;
        } catch (Exception e) {
            LOGGER.error("HTTP请求异常, uri:{},parameters:{}", uri,parameters);
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
        return responseMap;
    }

    /**
     * Send Http
     *
     * @param requestBuilder
     * @param uri
     * @param headers
     * @param parameters
     * @return
     */
    public String doHttp(RequestBuilder requestBuilder, String uri, Map<String, String> headers, Map<String, String> parameters,boolean isLog,int timeOut) {
        String method = requestBuilder.getMethod();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,timeOut);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, parameters);
            httpResponse = httpClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            String response = null;
            if(statusCode==200){
            	 httpEntity = httpResponse.getEntity();
                 response = EntityUtils.toString(httpEntity, "UTF-8");
            }
            if(isLog) {
                LOGGER.info("{}, uri:{},parameters:{},response:{},statusCode:{}", method, uri, parameters,response,statusCode);
            }
            return response;
        } catch (Exception e) {
            LOGGER.error("{}, uri:{}, parameters:{}", method, uri, parameters, e);
            return null;
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
    }
    /**
     * Send Http
     *
     * @param requestBuilder
     * @param uri
     * @param headers
     * @param parameters
     * @return
     */
    public String doObjectHttp(RequestBuilder requestBuilder, String uri, Map<String, String> headers, Map<String, Object> parameters) {
        String method = requestBuilder.getMethod();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,TIME_OUT_60);
            this.setHeader(requestBuilder, headers);
            this.setParameter2(requestBuilder, parameters);
            httpResponse = httpClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            String response = null;
            if(statusCode==200){
            	 httpEntity = httpResponse.getEntity();
                 response = EntityUtils.toString(httpEntity, "UTF-8");
            }
            LOGGER.info("{}, uri:{}, parameters:{},response:{}", method, uri, parameters,response);
            return response;
        } catch (Exception e) {
            LOGGER.error("{}, uri:{}, parameters:{}", method, uri, parameters, e);
            return null;
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
    }
    /**
     * Send Http
     *
     * @param requestBuilder
     * @param uri
     * @param headers
     * @param jsonParams
     * @return
     */
    public String doJsonHttp(RequestBuilder requestBuilder, String uri, Map<String, String> headers, String jsonParams) {
        String method = requestBuilder.getMethod();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,TIME_OUT_60);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, jsonParams);
            httpResponse = httpClient.execute(requestBuilder.build());
            httpEntity = httpResponse.getEntity();
            String response = EntityUtils.toString(httpEntity, CharEncoding.UTF_8);
            LOGGER.info("{}, uri:{},parameters:{},response:{},statusCode:{}", method, uri, jsonParams,response,httpResponse.getStatusLine().getStatusCode());
            return response;
        } catch (Exception e) {
            LOGGER.error("{}, uri:{}, ", method, uri, e);
            return null;
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
    }

    @Override
    public String postNiwodai(String uri, Map<String, String> headers, Map<String, String> parameters, boolean isLog) {
        return doniwodaiHttps(RequestBuilder.post(),uri,headers,parameters,isLog,TIME_OUT_60);
    }

    @Override
    public String postNiwodaiJson(String uri,Map<String, String> headers, String jsonParams,boolean isLog) {
        return doniwodaiObjectHttps(RequestBuilder.post(),uri,headers,jsonParams,isLog,TIME_OUT_60);
    }

    public String doniwodaiObjectHttps(RequestBuilder requestBuilder, String uri, Map<String, String> headers, String jsonParams, boolean isLog, int timeOut){
        String method = requestBuilder.getMethod();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,timeOut);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, jsonParams);
            httpResponse = niwodaiHttpsClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            String response = null;
            if(statusCode==200){
                httpEntity = httpResponse.getEntity();
                response = EntityUtils.toString(httpEntity, "UTF-8");
            }
            if(isLog) {
                LOGGER.info("{}, uri:{},parameters:{},response:{},statusCode:{}", method, uri, jsonParams,response,statusCode);
            }
            return response;
        } catch (Exception e) {
            LOGGER.error("{}, uri:{}, parameters:{}", method, uri, jsonParams, e);
            return null;
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
    }

    public String doniwodaiHttps(RequestBuilder requestBuilder, String uri, Map<String, String> headers, Map<String, String> parameters, boolean isLog, int timeOut){
        String method = requestBuilder.getMethod();
        HttpEntity httpEntity = null;
        CloseableHttpResponse httpResponse = null;
        try {
            requestBuilder.setUri(uri);
            this.setConfig(requestBuilder,timeOut);
            this.setHeader(requestBuilder, headers);
            this.setParameter(requestBuilder, parameters);
            httpResponse = niwodaiHttpsClient.execute(requestBuilder.build());
            int statusCode = httpResponse.getStatusLine().getStatusCode();
            String response = null;
            if(statusCode==200){
                httpEntity = httpResponse.getEntity();
                response = EntityUtils.toString(httpEntity, "UTF-8");
            }
            if(isLog)
                LOGGER.info("{}, uri:{},parameters:{},response:{},statusCode:{}", method, uri, parameters,response,statusCode);
            return response;
        } catch (Exception e) {
            LOGGER.error("{}, uri:{}, parameters:{}", method, uri, parameters, e);
            return null;
        } finally {
            EntityUtils.consumeQuietly(httpEntity);
            IOUtils.closeQuietly(httpResponse);
        }
    }
    /**
     * Set Parameter
     *
     * @param requestBuilder
     * @param parameters
     */
    private void setParameter(RequestBuilder requestBuilder, Map<String, String> parameters) {
        if (MapUtils.isNotEmpty(parameters)) {
            parameters = Maps.filterValues(parameters, Predicates.notNull());

            Set<Map.Entry<String, String>> entrys = parameters.entrySet();
            /************** Get 请求 **************/
            if (requestBuilder.getMethod().equalsIgnoreCase(HttpGet.METHOD_NAME)) {
                for (Map.Entry<String, String> entry : entrys) {
                    requestBuilder.addParameter(entry.getKey(), entry.getValue());
                }
            }
            /************** Post 请求 **************/
            else {
                List<NameValuePair> valuePairs = new ArrayList<>();
                for (Map.Entry<String, String> entry : entrys) {
                    valuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
				try {
					requestBuilder.setEntity(new UrlEncodedFormEntity(valuePairs, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
            }
        }
    }
    
    /**
     * Set Parameter
     *
     * @param requestBuilder
     * @param parameters Map<String, Object>
     */
    private void setParameter2(RequestBuilder requestBuilder, Map<String, Object> parameters) {
        if (MapUtils.isNotEmpty(parameters)) {
            parameters = Maps.filterValues(parameters, Predicates.notNull());

            Set<Map.Entry<String, Object>> entrys = parameters.entrySet();
            /************** Get 请求 **************/
            if (requestBuilder.getMethod().equalsIgnoreCase(HttpGet.METHOD_NAME)) {
                for (Map.Entry<String, Object> entry : entrys) {
                    requestBuilder.addParameter(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
                }
            }
            /************** Post 请求 **************/
            else {
                List<NameValuePair> valuePairs = new ArrayList<>();
                for (Map.Entry<String, Object> entry : entrys) {
                    valuePairs.add(new BasicNameValuePair(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())));
                }
				try {
					requestBuilder.setEntity(new UrlEncodedFormEntity(valuePairs, "UTF-8"));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
            }
        }
    }
    /**
     * Set Parameter
     *
     * @param requestBuilder
     * @param jsonParams
     */
    private void setParameter(RequestBuilder requestBuilder, String jsonParams) {
        if (StringUtils.isNotEmpty(jsonParams)) {
        	StringEntity stringEntity = new StringEntity(jsonParams, CharEncoding.UTF_8);
        	stringEntity.setContentType("application/json; charset=utf-8");
            requestBuilder.setEntity(stringEntity);
        }
    }
    /**
     * Set Header
     *
     * @param requestBuilder
     * @param headers
     */
    private void setHeader(RequestBuilder requestBuilder, Map<String, String> headers) {
        requestBuilder.setHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36")
                .setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
                .setHeader("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
                .setHeader("Connection", "keep-alive");

        if (MapUtils.isNotEmpty(headers)) {
            headers = Maps.filterValues(headers, Predicates.notNull());

            Set<Map.Entry<String, String>> entrys = headers.entrySet();
            for (Map.Entry<String, String> entry : entrys) {
                requestBuilder.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * Request config
     *
     * @return
     */
    private void setConfig(RequestBuilder requestBuilder,int timeOut) {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(timeOut)
                .setSocketTimeout(timeOut)
                .setConnectionRequestTimeout(timeOut)
                .build();
        requestBuilder.setConfig(config);
    }
}
