package org.springblade.modules.app.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
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.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class HttpUtils {

	private static final int timeout = 30000;//超时时间30s

	public static String doPostWithXml(String url, String xmlStr, String charset){
		log.info("request: url= " + url);
		log.info("request: param= "+ xmlStr);
		try {
			HttpClient httpClient = new HttpClient();
			PostMethod postMethod = new PostMethod(url);
			httpClient.getParams().setSoTimeout(timeout);

			postMethod.setRequestEntity(new StringRequestEntity(xmlStr, "text/xml", charset));
			int status = httpClient.executeMethod(postMethod);
			if(status == HttpStatus.SC_OK){
				BufferedInputStream bis = new BufferedInputStream(postMethod.getResponseBodyAsStream());
				byte[] bytes = new byte[1024];
				ByteArrayOutputStream bos = new ByteArrayOutputStream();
				int count = 0;
				while ((count=bis.read(bytes))!=-1) {
					bos.write(bytes, 0, count);
				}
				byte[] strByte = bos.toByteArray();
				String responseXml = new String(strByte, 0, strByte.length, charset);
				log.info("response: " + responseXml);

				bos.close();
				bis.close();
				postMethod.releaseConnection();
				httpClient.getHttpConnectionManager().closeIdleConnections(0);
				return responseXml;
			}
			return null;
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}

	public static String doGet(String url, LinkedHashMap<String, String> params, String charset){
		return doGet(url, map2String(params), charset);
	}

	public static String doGet(String url, LinkedHashMap<String, String> params){
		return doGet(url, map2String(params), "utf-8");
	}


	public static String doGet(String url, Map<String, Object> params){
		return doGet(url, map2StringObjectValue(params), "utf-8");
	}


	public static String doGet(String url, String params){
		return doGet(url, params, "utf-8");
	}

	public static String doPost(String url, LinkedHashMap<String, String> params){
		return doPost(url, params, "utf-8");
	}

	public static String doPost(String url, LinkedHashMap<String, String> params, String charset){
		try {
			HttpMethod postMethod = initPostMethod(url, params, charset);
			return getResponseBody(doHttpRequest(postMethod), charset);
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}

	public static String doPost(String url, String data){
		return doPost(url, data, "utf-8");
	}

	public static String doPost(String url, String data, String charset){
		try {
			HttpMethod postMethod = initPostMethod(url, data, charset);
			return getResponseBody(doHttpRequest(postMethod), charset);
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}

	public static String doGet(String url, String params, String charset){
		try {
			HttpMethod getMethod = initGetMethod(url, params);
			return getResponseBody(doHttpRequest(getMethod), charset);
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}

	public static InputStream doHttpRequest(HttpMethod httpMethod){
		try {
			HttpClient httpClient = new HttpClient();
			httpClient.getParams().setSoTimeout(timeout);

			int status = httpClient.executeMethod(httpMethod);
			if(status == HttpStatus.SC_OK){
				httpClient.getHttpConnectionManager().closeIdleConnections(0);
				return httpMethod.getResponseBodyAsStream();
			}
			return null;
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}

	public static InputStream doHttpRequest(String url) throws IOException{
		return doHttpRequest(initGetMethod(url, null));
	}

	public static String getResponseBody(InputStream is, String charset){
		if (is==null) {
			return null;
		}
		try {
			BufferedInputStream bis = new BufferedInputStream(is);
			byte[] bytes = new byte[1024];
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			int count = 0;
			while ((count=bis.read(bytes))!=-1) {
				bos.write(bytes, 0, count);
			}
			byte[] strByte = bos.toByteArray();
			String responseBody = new String(strByte, 0, strByte.length, charset);
			log.info("response: " + responseBody);

			bos.close();
			bis.close();
			return responseBody;
		} catch (Exception e) {
			log.error("Exception occurs: ", e);return null;
		} finally {
			if(is!=null){
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private static HttpMethod initGetMethod(String url, String param) throws IOException{
		GetMethod get = null;
		if(StringUtils.isBlank(param)){
			get = new GetMethod(url);
		}else{
			get = new GetMethod(url+"?"+param);
		}
        get.releaseConnection();
        return get;
    }

	public static String map2String(LinkedHashMap<String, String> params){
		try {
			if(params == null || params.isEmpty()){
				return null;
			}
			StringBuilder sb = new StringBuilder();
			for (Map.Entry<String,String> entry : params.entrySet()) {
				sb.append(entry.getKey());
				sb.append("=");
				sb.append(entry.getValue());
				sb.append("&");
			}
			sb.deleteCharAt(sb.length()-1);
			return sb.toString();
		} catch (Exception e) {
			log.error("Exception occurs: ", e);
			return null;
		}
	}


	public static String map2StringObjectValue(Map<String, Object> params){
		try {
			if(params == null || params.isEmpty()){
				return null;
			}
			StringBuilder sb = new StringBuilder();
			for (String key : params.keySet()) {
				sb.append(key);
				sb.append("=");
				sb.append(URLEncoder.encode(""+params.get(key),"UTF-8"));
				sb.append("&");
			}
			sb.deleteCharAt(sb.length()-1);
			return sb.toString();
		} catch (Exception e) {
			log.error("Exception occurs: ", e);return null;
		}
	}

    private static HttpMethod initPostMethod(String url, LinkedHashMap<String, String> params, String charset) throws IOException{
		PostMethod post = new PostMethod(url);
		post.setRequestHeader("Content-Type","application/x-www-form-urlencoded;charset=" + charset);
		NameValuePair[] paramsArr = null;
		if(params != null && !params.isEmpty()){
			paramsArr = new NameValuePair[params.size()];
			int i = 0;
			for (Map.Entry<String,String> entry : params.entrySet()) {
				paramsArr[i++] = new NameValuePair(entry.getKey(),entry.getValue());
			}
		}
		post.setRequestBody(paramsArr);
		post.releaseConnection();
		return post;
	}

    private static HttpMethod initPostMethod(String url, String data, String charset) throws IOException{
		PostMethod post = new PostMethod(url);
		RequestEntity entity = new StringRequestEntity(data, "application/json","UTF-8");
		post.setRequestEntity(entity);
		post.setRequestHeader("Content-Type", "application/json;charset=" + charset);
		post.releaseConnection();
		return post;
	}

	/**
	 * @description
	 *      获取请求数据
	 * @author  majunjie
	 * @date 2017/7/4 17:35
	 */
	public static String getRequestPostStr(HttpServletRequest request){
		try {
			byte buffer[] = getRequestPostBytes(request);
			String charEncoding = request.getCharacterEncoding();
			if (charEncoding == null) {
				charEncoding = "UTF-8";
			}
			return new String(buffer, charEncoding);
		} catch (IOException e) {
			log.warn("获取请求参数异常",e);
			return null;
		}
	}

	/**
	 * @description
	 *      获取请求字节数组
	 * @author  majunjie
	 * @date 2017/7/4 17:35
	 */
	public static byte[] getRequestPostBytes(HttpServletRequest request)
			throws IOException {
		int contentLength = request.getContentLength();
		if(contentLength<0){
			return null;
		}
		byte buffer[] = new byte[contentLength];
		for (int i = 0; i < contentLength;) {

			int readlen = request.getInputStream().read(buffer, i,
					contentLength - i);
			if (readlen == -1) {
				break;
			}
			i += readlen;
		}
		return buffer;
	}

	/**
	 * get
	 *
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doGet(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpGet request = new HttpGet(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		return httpClient.execute(request);
	}

	/**
	 * post form
	 *
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param bodys
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      Map<String, String> bodys)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpPost request = new HttpPost(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (bodys != null) {
			List<org.apache.http.NameValuePair> nameValuePairList = new ArrayList<org.apache.http.NameValuePair>();

			for (String key : bodys.keySet()) {
				nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
			}
			UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
			formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
			request.setEntity(formEntity);
		}

		return httpClient.execute(request);
	}

	/**
	 * Post String
	 *
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      String body)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpPost request = new HttpPost(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (StringUtils.isNotBlank(body)) {
			request.setEntity(new StringEntity(body, "utf-8"));
		}

		return httpClient.execute(request);
	}

	/**
	 * Post stream
	 *
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPost(String host, String path, String method,
                                      Map<String, String> headers,
                                      Map<String, String> querys,
                                      byte[] body)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpPost request = new HttpPost(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (body != null) {
			request.setEntity(new ByteArrayEntity(body));
		}

		return httpClient.execute(request);
	}

	/**
	 * Put String
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     String body)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpPut request = new HttpPut(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (StringUtils.isNotBlank(body)) {
			request.setEntity(new StringEntity(body, "utf-8"));
		}

		return httpClient.execute(request);
	}

	/**
	 * Put stream
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doPut(String host, String path, String method,
                                     Map<String, String> headers,
                                     Map<String, String> querys,
                                     byte[] body)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpPut request = new HttpPut(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		if (body != null) {
			request.setEntity(new ByteArrayEntity(body));
		}

		return httpClient.execute(request);
	}

	/**
	 * Delete
	 *
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @return
	 * @throws Exception
	 */
	public static HttpResponse doDelete(String host, String path, String method,
                                        Map<String, String> headers,
                                        Map<String, String> querys)
			throws Exception {
		org.apache.http.client.HttpClient httpClient = wrapClient(host);

		HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
		for (Map.Entry<String, String> e : headers.entrySet()) {
			request.addHeader(e.getKey(), e.getValue());
		}

		return httpClient.execute(request);
	}

	private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
		StringBuilder sbUrl = new StringBuilder();
		sbUrl.append(host);
		if (!StringUtils.isBlank(path)) {
			sbUrl.append(path);
		}
		if (null != querys) {
			StringBuilder sbQuery = new StringBuilder();
			for (Map.Entry<String, String> query : querys.entrySet()) {
				if (0 < sbQuery.length()) {
					sbQuery.append("&");
				}
				if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
					sbQuery.append(query.getValue());
				}
				if (!StringUtils.isBlank(query.getKey())) {
					sbQuery.append(query.getKey());
					if (!StringUtils.isBlank(query.getValue())) {
						sbQuery.append("=");
						sbQuery.append(URLEncoder.encode(query.getValue(), "utf-8"));
					}
				}
			}
			if (0 < sbQuery.length()) {
				sbUrl.append("?").append(sbQuery);
			}
		}

		return sbUrl.toString();
	}

	private static org.apache.http.client.HttpClient wrapClient(String host) {
		org.apache.http.client.HttpClient httpClient = new DefaultHttpClient();
		if (host.startsWith("https://")) {
			sslClient(httpClient);
		}

		return httpClient;
	}

	private static void sslClient(org.apache.http.client.HttpClient httpClient) {
		try {
			SSLContext ctx = SSLContext.getInstance("TLS");
			X509TrustManager tm = new X509TrustManager() {
				@Override
				public X509Certificate[] getAcceptedIssuers() {
					return null;
				}
				@Override
				public void checkClientTrusted(X509Certificate[] xcs, String str) {

				}
				@Override
				public void checkServerTrusted(X509Certificate[] xcs, String str) {

				}
			};
			ctx.init(null, new TrustManager[] { tm }, null);
			SSLSocketFactory ssf = new SSLSocketFactory(ctx);
			ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
			ClientConnectionManager ccm = httpClient.getConnectionManager();
			SchemeRegistry registry = ccm.getSchemeRegistry();
			registry.register(new Scheme("https", 443, ssf));
		} catch (KeyManagementException ex) {
			throw new RuntimeException(ex);
		} catch (NoSuchAlgorithmException ex) {
			throw new RuntimeException(ex);
		}
	}


	/**
	 * 模拟请求
	 *
	 * @param url		资源地址
	 * @param map	参数列表
	 * @param encoding	编码
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static String sendIgnoreVerifySSL(String url, Map<String,String> map, String encoding) throws KeyManagementException, NoSuchAlgorithmException, ClientProtocolException, IOException {
		String body = "";
		//采用绕过验证的方式处理https请求
		SSLContext sslcontext = createIgnoreVerifySSL();

		// 设置协议http和https对应的处理socket链接工厂的对象
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https", new SSLConnectionSocketFactory(sslcontext))
				.build();
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		HttpClients.custom().setConnectionManager(connManager);

		//创建自定义的httpclient对象
		CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
//		CloseableHttpClient client = HttpClients.createDefault();

		//创建post方式请求对象
		HttpPost httpPost = new HttpPost(url);

		//装填参数
		List<org.apache.http.NameValuePair> nvps = new ArrayList<org.apache.http.NameValuePair>();
		if(map!=null){
			for (Map.Entry<String, String> entry : map.entrySet()) {
				nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
			}
		}
		//设置参数到请求对象中
		httpPost.setEntity(new UrlEncodedFormEntity(nvps, encoding));


		//设置header信息
		//指定报文头【Content-type】、【User-Agent】
		httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
		httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

		//执行请求操作，并拿到结果（同步阻塞）
		CloseableHttpResponse response = client.execute(httpPost);
		//获取结果实体
		HttpEntity entity = response.getEntity();
		if (entity != null) {
			//按指定编码转换结果实体为String类型
			body = EntityUtils.toString(entity, encoding);
		}
		EntityUtils.consume(entity);
		//释放链接
		response.close();
		return body;
	}

	/**
	 * 绕过验证
	 *
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws KeyManagementException
	 */
	public static SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
		SSLContext sc = SSLContext.getInstance("SSLv3");

		// 实现一个X509TrustManager接口，用于绕过验证，不用修改里面的方法
		X509TrustManager trustManager = new X509TrustManager() {
			@Override
			public void checkClientTrusted(
					X509Certificate[] paramArrayOfX509Certificate,
					String paramString) throws CertificateException {
			}

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

			@Override
			public X509Certificate[] getAcceptedIssuers() {
				return null;
			}
		};

		sc.init(null, new TrustManager[] { trustManager }, null);
		return sc;
	}

}

