/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved.
 * <a>http://www.crunii.com</a>
 */
package com.crunii.micro.common.spring.rest;

import com.crunii.micro.common.http.handler.HttpReqErrorHandler;
import com.crunii.micro.common.http.interceptor.LogHttpReqInterceptor;
import com.crunii.micro.common.http.interceptor.LogHttpRspInterceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpHost;
import org.apache.http.client.HttpClient;
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.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 田平 create 2021/9/28 16:55
 */
public class RestBuilder {

	/**
	 * 注意RestTemplate 是线程安全的，可以复用，
	 * 构建代价非常大，不要每次都构建一个RestTemplate
	 *
	 * @return
	 */
	public static RestTemplate buildRestTemplate() {
		return buildRestTemplate(RestConfig.builder().build());
	}

	/**
	 * 支持动态设置超时时间
	 *
	 * @return
	 */
	public static TimeoutRestTemplate buildTimeoutRestTemplate() {
		return buildTimeoutRestTemplate(RestConfig.builder().build());
	}

	/**
	 * 注意RestTemplate 是线程安全的，可以复用，
	 * 构建代价非常大，不要每次都构建一个RestTemplate
	 *
	 * @param restConfig
	 * @return
	 */
	public static RestTemplate buildRestTemplate(RestConfig restConfig) {
		HttpClient httpClient = getHttpClient(restConfig);
		HttpComponentsClientHttpRequestFactory hf = new HttpComponentsClientHttpRequestFactory(httpClient);
		hf.setConnectionRequestTimeout(restConfig.getRequestTimeout());
		hf.setConnectTimeout(restConfig.getConnectTimeout());
		hf.setReadTimeout(restConfig.getSocketTimeout());

		RestTemplate template = new RestTemplate(hf);
		List<HttpMessageConverter<?>> converters = template.getMessageConverters();
		addConverters(restConfig.getMapper(), converters);
		return template;
	}

	public static TimeoutRestTemplate buildTimeoutRestTemplate(RestConfig restConfig) {
		HttpClient httpClient = getHttpClient(restConfig);

		TimeoutHttpClientRequestFactory hf = new TimeoutHttpClientRequestFactory(httpClient);
		hf.setConnectionRequestTimeout(restConfig.getRequestTimeout());
		hf.setConnectTimeout(restConfig.getConnectTimeout());
		hf.setReadTimeout(restConfig.getSocketTimeout());

		TimeoutRestTemplate template = new TimeoutRestTemplate(hf);
		List<HttpMessageConverter<?>> converters = template.getMessageConverters();
		addConverters(restConfig.getMapper(), converters);
		return template;
	}

	public static void addConverters(ObjectMapper mapper, List<HttpMessageConverter<?>> converters) {
		//防止乱码
		int index = -1;
		for (int i = 0; i < converters.size(); i++) {
			if (converters.get(i) instanceof StringHttpMessageConverter) {
				index = i;
				break;
			}
		}
		if (index > -1) {
			converters.set(index, new StringHttpMessageConverter(StandardCharsets.UTF_8));
		}
		//设置自定义的jsonMapper
		List<HttpMessageConverter<?>> cs = converters.stream()
				.filter(h -> h instanceof MappingJackson2HttpMessageConverter)
				.collect(Collectors.toList());
		if (cs.size() > 0) {
			cs.stream().forEach(h -> {
				MappingJackson2HttpMessageConverter converter = (MappingJackson2HttpMessageConverter) h;
				converter.setObjectMapper(mapper);
			});
		} else {
			converters.add(new MappingJackson2HttpMessageConverter(mapper));
		}
	}

	private static HttpClient getHttpClient(RestConfig restConfig) {
		RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.create();
		ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory();
		registryBuilder.register("http", plainSF);
		PoolingHttpClientConnectionManager cm = null;
		try {
			KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
			SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustAllStrategy()).build();
			LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
			registryBuilder.register("https", sslSF);
			Registry<ConnectionSocketFactory> registry = registryBuilder.build();
			cm = new PoolingHttpClientConnectionManager(registry);
			cm.setMaxTotal(restConfig.getMaxTotal());
			cm.setDefaultMaxPerRoute(restConfig.getMaxPerRoute());
		} catch (Exception e) {
			throw new RuntimeException("RestTemplate build failure!", e);
		}

		HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
		if (restConfig.isEnabledLog()) {
			LogHttpReqInterceptor reqInterceptor = new LogHttpReqInterceptor();
			LogHttpRspInterceptor rspInterceptor = new LogHttpRspInterceptor();

			httpClientBuilder.setRetryHandler(new HttpReqErrorHandler())
					.setDefaultHeaders(restConfig.getDefaultHeaders())
					.addInterceptorFirst(reqInterceptor)
					.addInterceptorFirst(rspInterceptor);
		}

		//设置http代理服务器192.168.149.189:8889
		if (StringUtils.hasText(restConfig.getProxyServerAddress())) {
			httpClientBuilder.setProxy(HttpHost.create(restConfig.getProxyServerAddress()));
		}

		httpClientBuilder.setConnectionManager(cm);
		return httpClientBuilder.build();
	}

	public static void main(String[] args) {
//        RestTemplate rt = RestBuilder.buildRestTemplate();
//        Map<String, Object> map = new HashMap<>();
//        map.put("username", "ABCD");
//        ResponseEntity<Result> resultResponseEntity = rt.postForEntity("http://localhost:9995/TestController/testBody", map, Result.class);
//        System.out.println(resultResponseEntity.getBody());
		HttpHost httpHost = HttpHost.create("192.168.149.189:8889");
		System.out.println(httpHost);
	}
}
