/**
 * $Id$
 * Copyright(C) 2015-2020 kowlone - internet center, All Rights Reserved.
 */
package com.liquidnet.commons.lang.util;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
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.client.utils.URIBuilder;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.MessageConstraints;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
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 javax.net.ssl.SSLContext;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.CodingErrorAction;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;

/**
 * HTTP工具类
 *
 * @author <a href="kowlone2006@163.com">kowlone</a>
 * @version 1.0 2012-5-4 下午2:15:40
 */
public abstract class HttpUtil {

	private static CloseableHttpClient httpclient = null;
	private static CloseableHttpClient httpsClient = null;

	static {
		try {

			RequestConfig requestConfig = RequestConfig.custom()
					.setConnectionRequestTimeout(5000)
					.setSocketTimeout(30000)
					.setConnectTimeout(30000)
					.setExpectContinueEnabled(false)
					.build();

			MessageConstraints messageConstraints = MessageConstraints.custom()
					.setMaxHeaderCount(200)
					.setMaxLineLength(2000)
					.build();

			ConnectionConfig connectionConfig = ConnectionConfig.custom()
					.setMalformedInputAction(CodingErrorAction.IGNORE)
					.setUnmappableInputAction(CodingErrorAction.IGNORE)
					.setCharset(Consts.UTF_8)
					.setMessageConstraints(messageConstraints)
					.build();

			PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
			connectionManager.setDefaultConnectionConfig(connectionConfig);
			connectionManager.setMaxTotal(100);// 连接池最大并发连接数
			connectionManager.setDefaultMaxPerRoute(10);// 单路由最大并发数

			httpclient = HttpClients.custom().setConnectionManager(connectionManager).setDefaultRequestConfig(requestConfig).build();

			try {
				SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
					//信任所有
					@Override
                    public boolean isTrusted(X509Certificate[] chain,
                                             String authType) throws CertificateException {
						return true;
					}
				}).build();

				RequestConfig requestConfigHttps = RequestConfig.custom()
						.setConnectionRequestTimeout(5000)
						.setSocketTimeout(30000)
						.setConnectTimeout(30000)
						.setExpectContinueEnabled(false)
						.build();

				MessageConstraints messageConstraintsHttps = MessageConstraints.custom()
						.setMaxHeaderCount(200)
						.setMaxLineLength(2000)
						.build();

				ConnectionConfig connectionConfigHttps = ConnectionConfig.custom()
						.setMalformedInputAction(CodingErrorAction.IGNORE)
						.setUnmappableInputAction(CodingErrorAction.IGNORE)
						.setCharset(Consts.UTF_8)
						.setMessageConstraints(messageConstraintsHttps)
						.build();

				PoolingHttpClientConnectionManager connectionManagerHttps = new PoolingHttpClientConnectionManager();
				connectionManager.setDefaultConnectionConfig(connectionConfigHttps);
				connectionManager.setMaxTotal(100);// 连接池最大并发连接数
				connectionManager.setDefaultMaxPerRoute(10);// 单路由最大并发数
				SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
				httpsClient = HttpClients.custom().setSSLSocketFactory(sslsf).setConnectionManager(connectionManagerHttps).setDefaultRequestConfig(requestConfigHttps).build();
			} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
				e.printStackTrace();
			}

		} catch (Exception e) {
		    e.printStackTrace();
			//logger.error("create httpUtil error", e);
		}
	}

	public static String get(String scheme, String host, String path, List<NameValuePair> uriParam, List<NameValuePair> header,
			Charset charset) throws IOException {
		URIBuilder uriBuilder = new URIBuilder().setScheme(scheme).setHost(host).setPath(path);
		if (uriParam != null) {
            uriBuilder.setParameters(uriParam);
        }

		try {
			HttpGet httpget = new HttpGet(uriBuilder.build());
			if (header != null) {
				for (NameValuePair nvp : header) {
					httpget.setHeader(nvp.getName(), nvp.getValue());
				}
			}
			ResponseHandler<String> responseHandler = getResponseHandler(charset, null);

			return httpclient.execute(httpget, responseHandler);
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	public static String get(String scheme, String host, String path, List<NameValuePair> uriParam, List<NameValuePair> header)
			throws IOException {
		return get(scheme, host, path, uriParam, header, Consts.UTF_8);
	}

	public static String get(HttpGet httpGet, Charset charset) throws IOException {
		ResponseHandler<String> responseHandler = getResponseHandler(charset, null);
	//	logger.debug("get data is :{}", httpGet.toString());
		return httpclient.execute(httpGet, responseHandler);
	}

	public static String get(HttpGet httpGet) throws IOException {
		return get(httpGet, Consts.UTF_8);
	}

	public static byte[] binaryGet(HttpGet httpGet) throws IOException {
		CloseableHttpResponse response = httpclient.execute(httpGet);
		return EntityUtils.toByteArray(response.getEntity());
	}

	public static String post(HttpPost httpPost, Charset charset) throws IOException {
		ResponseHandler<String> responseHandler = getResponseHandler(charset, null);
		return httpclient.execute(httpPost, responseHandler);
	}

	public static String post(HttpPost httpPost) throws IOException {
		return post(httpPost, Consts.UTF_8);
	}

	public static String post(String uri, List<? extends NameValuePair> params, final OKResponseHandler okResponseHandler)
			throws IOException {
		HttpPost post = new HttpPost(uri);
		post.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
		return httpclient.execute(post, getResponseHandler(Consts.UTF_8, okResponseHandler));
	}

	public static String postHttps(String uri, List<? extends NameValuePair> params, final OKResponseHandler okResponseHandler)
			throws IOException {
		HttpPost post = new HttpPost(uri);
		post.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));
		return httpsClient.execute(post, getResponseHandler(Consts.UTF_8, okResponseHandler));
	}

	public static byte[] binaryPost(HttpPost httpPost) throws IOException {
		CloseableHttpResponse response = httpclient.execute(httpPost);

		return EntityUtils.toByteArray(response.getEntity());
	}

	/**
	 * 将原来get方法中ResponseHandler提成方法，由get/post共用
	 *
	 * @return
	 */
	private static ResponseHandler<String> getResponseHandler(Charset charset, OKResponseHandler okResponseHandler) {
		ResponseHandler<String> responseHandler = response -> {

			int status = response.getStatusLine().getStatusCode();
			if (status >= 200 && status < 300) {
				HttpEntity entity = response.getEntity();
				if (okResponseHandler != null) {
                    okResponseHandler.handler(response);
                }
				return entity != null ? EntityUtils.toString(entity, charset) : null;
			} else {
				//logger.error(EntityUtils.toString(response.getEntity()));
				throw new ClientProtocolException("Unexpected response status: " + status);
			}
		};
		return responseHandler;
	}


	public interface OKResponseHandler {
		void handler(HttpResponse response);
	}
}
