package com.bcxin.ins.third.gzzrx.changan.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
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.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
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.TrustSelfSignedStrategy;
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.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

public class CAHttpUtils {
	
	/**
	 * get
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @return
	 * @throws Exception
	 */
	public static CloseableHttpResponse doGet(String host, String path, String method, 
			Map<String, String> headers, 
			Map<String, String> querys,
			Map<String, String> sslConfig)
            throws Exception {    	
		CloseableHttpClient httpClient = wrapClient(host, sslConfig);

    	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 CloseableHttpResponse doPost(String host, String path, String method, 
			Map<String, String> headers, 
			Map<String, String> querys, 
			Map<String, String> bodys,
			Map<String, String> sslConfig)
            throws Exception {    	
		CloseableHttpClient httpClient = wrapClient(host, sslConfig);

    	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<NameValuePair> nameValuePairList = new ArrayList<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 CloseableHttpResponse doPost(String host, String path, String method, 
			Map<String, String> headers, 
			Map<String, String> querys, 
			String body,
			Map<String, String> sslConfig)
            throws Exception {    	
		CloseableHttpClient httpClient = wrapClient(host, sslConfig);

    	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, "gbk"));
        }

        return httpClient.execute(request);
    }
	
	/**
	 * Post stream
	 * 
	 * @param host
	 * @param path
	 * @param method
	 * @param headers
	 * @param querys
	 * @param body
	 * @return
	 * @throws Exception
	 */
	public static CloseableHttpResponse doPost(String host, String path, String method, 
			Map<String, String> headers, 
			Map<String, String> querys, 
			byte[] body,
			Map<String, String> sslConfig)
            throws Exception {    	
		CloseableHttpClient httpClient = wrapClient(host, sslConfig);

    	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);
    }
	
	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 CloseableHttpClient wrapClient(String host, Map<String, String> sslConfig) throws Exception {
		CloseableHttpClient httpClient = null ;
		SSLContext sslcontext = null;
		String ignoreVerifySSL = sslConfig != null ? sslConfig.get("IgnoreVerifySSL") : "";
		if(StringUtils.equals("true", ignoreVerifySSL)) {
			sslcontext = createIgnoreVerifySSL();
		} else {
			String keyStorePath= sslConfig.get("keyStorePath");
			String keyStorepass= sslConfig.get("keyStorepass");
			String trustStorePath= sslConfig.get("trustStorePath");
			String trustStorepass= sslConfig.get("trustStorepass");
			// 使用双向认证
			sslcontext = createCustomSSL(keyStorePath, keyStorepass, trustStorePath, trustStorepass);
			// 使用单向认证
//			sslcontext = createCustomSSL("", "", trustStorePath, trustStorepass);

		}
		// 设置协议http和https对应的处理socket链接工厂的对象
		/*
		 *  如果出现 Certificate for <> doesn't match any of the subject alternative names: []
		 *  升级JDk版本为1.7以上
		 *   或者使用  register("https", new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE)) 忽略域名验证
		 */
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.INSTANCE)
            .register("https", new SSLConnectionSocketFactory(sslcontext))
//            .register("https", new SSLConnectionSocketFactory(sslcontext, NoopHostnameVerifier.INSTANCE))
            .build();
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        HttpClients.custom().setConnectionManager(connManager);
        httpClient = HttpClients.custom().setConnectionManager(connManager).build();
        
        return httpClient;
	}
	
	public static SSLContext createIgnoreVerifySSL() throws Exception {
		SSLContext sc = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
		return sc;
	}
	
	public static SSLContext createCustomSSL(String keyStorePath, String keyStorepass, String trustStorePath, String trustStorePass){
		SSLContext sc = null;
		FileInputStream trustInstream = null;
		KeyStore trustStore = null;
		FileInputStream keyInstream = null;
		KeyStore keyStore = null;
		try {
			trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			trustInstream = new FileInputStream(new File(trustStorePath));
			trustStore.load(trustInstream, trustStorePass.toCharArray());
			// 如果keyStorePath为空或者为空字符串，则认为使用单向认证
			boolean unilateralFlag = StringUtils.isBlank(keyStorePath) ? true : false;
			if(unilateralFlag) {
				// 相信自己的CA和所有自签名的证书
				sc = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
			} else {
				keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
				keyInstream = new FileInputStream(new File(keyStorePath));
				keyStore.load(keyInstream, keyStorepass.toCharArray());
				// 加载证书
				sc = SSLContexts.custom().loadKeyMaterial(keyStore, keyStorepass.toCharArray())
										 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
										 .build();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(trustInstream != null) {
					trustInstream.close();
				}
				
				if(keyInstream != null) {
					keyInstream.close();
				}
				
			} catch (IOException e) {
				
			}
		}
		return sc;
	}
	
	
    public static void main(String[] args) throws Exception { 
		String keyStorePath= "C:/Users/l/Desktop/fsdownload/demo/testjks/test_keystore.jks";
		String keyStorepass= "ecaictest";
		String trustStorePath= "C:/Users/l/Desktop/fsdownload/demo/testjks/test_truststore.jks";
		String trustStorepass= "ecaictest";
		
		Map<String, String> sslConfig = new HashMap<String, String>();
		sslConfig.put("IgnoreVerifySSL", "false");
		sslConfig.put("keyStorePath", keyStorePath);
		sslConfig.put("keyStorepass", keyStorepass);
		sslConfig.put("trustStorePath", trustStorePath);
		sslConfig.put("trustStorepass", trustStorepass);
		
        String reqxml = "<?xml version=\"1.0\" encoding=\"GBK\" standalone=\"yes\"?><root><applicant><age>33</age><appliName>邓婷</appliName><appliType>1</appliType><birthDay>1987-06-18</birthDay><identifyNumber>440281198706186626</identifyNumber><identifyType>01</identifyType><insuredIdentity>50</insuredIdentity><sex>2</sex><taxInfo><invoiceType>3</invoiceType><taxpayerType>4</taxpayerType></taxInfo><taxInfoFlag>1</taxInfoFlag></applicant><channel><channelAreaCode>M33000000030</channelAreaCode><channelCode>330020</channelCode><channelComCode>33000000</channelComCode><channelName>保险师</channelName><channelProductCode>27080210001</channelProductCode><channelTradeCode>0001</channelTradeCode><channelTradeDate>2021-02-23</channelTradeDate><channelTradeSerialNo>e0bbb14b83984e179abdc125b966ed10-005</channelTradeSerialNo><sourceType>0</sourceType></channel><main><endDate>2022-02-26 23:59:59</endDate><endHour>24</endHour><insuredCount>1</insuredCount><proposalNo>2021022315111173010863665250</proposalNo><startDate>2021-02-27 00:00:00</startDate><startHour>00</startHour><sumAmount>343000.0</sumAmount><sumPremium>350.0</sumPremium><sumQuantity>1</sumQuantity></main><insureds><insured><age>5</age><appliIdentity>50</appliIdentity><birthDay>2015-04-22</birthDay><identifyNumber>341203201504220315</identifyNumber><identifyType>01</identifyType><insuredCName>李泽含</insuredCName><sex>1</sex></insured></insureds></root>"; 
//        String host = "https://test.channelplat.capli.com.cn:5443/17104/commlife";
        String host = "http://test.channelplat.capli.com.cn:7000/17104/commlife1";
        Map<String, String> headers = new HashMap<String, String>();
        headers.put("Content-Type", "text/xml;charset=gbk");
        System.out.println("请求地址:" + host);
        System.out.println("请求报文:" + reqxml);
        CloseableHttpResponse httpResponse = CAHttpUtils.doPost(host, null, null, headers, null, reqxml, sslConfig);
        //获取结果实体
        String body = "";
        System.out.println("返回响应状态信息:" + httpResponse.getStatusLine());
  		HttpEntity entity = httpResponse.getEntity();
  		if (entity != null) {
  			//按指定编码转换结果实体为String类型
  			body = EntityUtils.toString(entity, "GBK");
  		}
  		EntityUtils.consume(entity);
  		//释放链接
  		httpResponse.close();
  		System.out.println("返回响应信息体:" + StringUtils.replaceEach(body, new String[] {"\n","\t","\r"}, new String[] {"","",""}));
  		//return body;
    
    }  
}