package org.springblade.modules.nh.factory.common;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springblade.common.filter.LoggingInterceptor;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.*;

public class EzTemplateBase extends RestTemplate {

    private final RestTemplate restTemplate;

    public EzTemplateBase(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public EzTemplateBase() {
        this.restTemplate = new RestTemplate(getClientHttpRequestFactory());
    }

    protected static RestTemplate createRestTemplate(ClientHttpRequestFactory factory) {
        ObjectMapper lax = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        MappingJackson2HttpMessageConverter c = new MappingJackson2HttpMessageConverter();
        c.setObjectMapper(lax);
        RestTemplate restTemplate = new RestTemplate(factory);

        List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
        if (CollectionUtils.isEmpty(interceptors)) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(new LoggingInterceptor());
        restTemplate.setInterceptors(interceptors);

        Iterator<HttpMessageConverter<?>> iterator = restTemplate.getMessageConverters().iterator();
        while (iterator.hasNext()) {
            HttpMessageConverter<?> next = iterator.next();
            if (next instanceof MappingJackson2HttpMessageConverter) {
                iterator.remove();
                break;
            }
        }
        restTemplate.getMessageConverters().add(c);
        return restTemplate;
    }

    public <T> T postForm(String url, Map<String, Object> data, Class<T> responseType, Object... uriVariables) throws RestClientException {
        long start = System.currentTimeMillis();
        MultiValueMap<String, Object> postData = new LinkedMultiValueMap<>();
        if (data != null) {
            postData.setAll(data);
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(postData, headers);
        T ret = restTemplate.postForObject(url, entity, responseType, uriVariables);
        logger.info(String.format("post to %s consumed: %s", url, (System.currentTimeMillis() - start)));
        return ret;
    }

    private <T> T doPostForm(String url, ParameterizedTypeReference<T> responseType, HttpEntity<MultiValueMap<String, Object>> entity, Object[] uriVariables) {
        return restTemplate.exchange(url, HttpMethod.POST, entity, responseType, uriVariables).getBody();
    }

    public <T> T postJson(String url, Map<String, Object> data, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
        return postJson(url, new ValueHolder<Object>(data), responseType, uriVariables);
    }

    public <T> T get(String url, Map<String, ?> uriVariables, ParameterizedTypeReference<T> responseType) throws RestClientException {
        HttpHeaders headers = new HttpHeaders();
        HttpHeaders httpHeaders = HttpHeaders.writableHttpHeaders(headers);
        callHeaders(url, null, httpHeaders);
        HttpEntity<String> entity = new HttpEntity<>(null, httpHeaders);
        return restTemplate.exchange(url, HttpMethod.GET, entity, responseType, uriVariables).getBody();
    }

	public <T> T get(int index,String url, Map<String, ?> uriVariables, ParameterizedTypeReference<T> responseType) throws RestClientException {
		HttpHeaders headers = new HttpHeaders();
		HttpHeaders httpHeaders = HttpHeaders.writableHttpHeaders(headers);
		callHeaders(url, null, httpHeaders);
		HttpEntity<String> entity = new HttpEntity<>(null, httpHeaders);
		return restTemplate.exchange(url, HttpMethod.GET, entity, responseType, uriVariables).getBody();
	}

    public <T> T postJson(String url, Object data, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
        return postJson(url, new ValueHolder<>(data), responseType, uriVariables);
    }

	public <T> T postJson(int index,String url, Object data, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
        return postJson(url, new ValueHolder<>(data), responseType, uriVariables);
    }

    public <T> T postJson(String url, Object data, Class<T> responseType, Object... uriVariables) throws RestClientException {
        return postJson(url, new ValueHolder<>(data), responseType, uriVariables);
    }

    private <T, K> T postJson(String url, ValueHolder<K> data, ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException {
        long start = System.currentTimeMillis();
        HttpHeaders headers = setJsonHttpHeaders();
        HttpEntity<K> entity = new HttpEntity<>(data.getData(), headers);
        T body = doPostJson(url, responseType, entity, uriVariables);
        logger.info(String.format("post to %s consumed: %s", url, (System.currentTimeMillis() - start)));
        return body;
    }

    protected <K> void callHeaders(String url, String data, HttpHeaders headers) {
    }

    private <T, K> T postJson(String url, ValueHolder<K> data, Class<T> responseType, Object... uriVariables) throws RestClientException {
        HttpHeaders headers = setJsonHttpHeaders();
        HttpEntity<K> entity = new HttpEntity<>(data.getData(), headers);
        long start = System.currentTimeMillis();
        T body = doPostJson(url, responseType, entity, uriVariables);
        logger.info(String.format("post to %s consumed: %s", url, (System.currentTimeMillis() - start)));
        return body;
    }

    private HttpHeaders setJsonHttpHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8"));
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        return headers;
    }

    private <T, K> T doPostJson(String url, ParameterizedTypeReference<T> responseType, HttpEntity<K> entity, Object[] uriVariables) {
        String body = new Gson().toJson(entity.getBody());
        HttpHeaders httpHeaders = HttpHeaders.writableHttpHeaders(entity.getHeaders());
        callHeaders(url, body, httpHeaders);
        url = changeUrl(url);
        HttpEntity<String> data = new HttpEntity<>(body, httpHeaders);
        return restTemplate.exchange(url, HttpMethod.POST, data, responseType, uriVariables).getBody();
    }

    protected String changeUrl(String url) {
        return url;
    }

    private <T, K> T doPostJson(String url, Class<T> responseType, HttpEntity<K> entity, Object[] uriVariables) {
        String body = new Gson().toJson(entity.getBody());
        HttpHeaders httpHeaders = HttpHeaders.writableHttpHeaders(entity.getHeaders());
        callHeaders(url, body, entity.getHeaders());
        HttpEntity<String> data = new HttpEntity<>(body, httpHeaders);
        return restTemplate.exchange(url, HttpMethod.POST, data, responseType, uriVariables).getBody();
    }

    static class ValueHolder<T> {
        private final T data;

        ValueHolder(T data) {
            this.data = data;
        }

        public T getData() {
            return data;
        }
    }

    protected static ClientHttpRequestFactory getClientHttpRequestFactory() {
        int timeout = 30000;
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(timeout)
                .setConnectionRequestTimeout(timeout)
                .setSocketTimeout(timeout).build();

        CloseableHttpClient client = HttpClientBuilder.create()
                .setDefaultRequestConfig(config)
                .setMaxConnPerRoute(1000)
                .setMaxConnTotal(1000)
                .build();



        return new SimpleClientHttpRequestFactory();
    }
}
