package com.cool.store.mq.util;

import com.alibaba.fastjson.JSONObject;
import com.cool.store.enums.ErrorCodeEnum;
import com.cool.store.exception.ServiceException;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;
import java.util.Objects;

@Service
public class HttpRestTemplateService {

    @Autowired
    private RestTemplate restTemplate;


    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 获取请求实体对象
     *
     * @param object
     * @param tenantAccessToken
     * @return
     */
    private HttpEntity getHttpEntity(Object object, String tenantAccessToken) {
        return new HttpEntity(object, this.getHeaders(tenantAccessToken));
    }

    public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables , Map<String, String> headMap) {
        logger.info("getForObject start:url={},responseType={},uriVariables={}", url, responseType.getName(),
                JSONObject.toJSONString(uriVariables));
        ResponseEntity<T> result = null;
        try {
            //封装请求头
            HttpHeaders headers = new HttpHeaders();
            headMap.forEach((k, v)->{
                headers.add(k, v);
            });
            HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(headers);
            result = exchange(url, HttpMethod.GET, formEntity, responseType, uriVariables);
        } catch (RestClientException e) {
            logger.error("getForObject error:{}", e);
            throw new ServiceException(ErrorCodeEnum.UNKNOWN);
        }
        logger.info("getForObject end:result={}", JSONObject.toJSONString(result));
        return result.getBody();
    }

    public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) {
        logger.info("getForObject start:url={},responseType={},uriVariables={}", url, responseType.getName(), JSONObject.toJSONString(uriVariables));
        T result = null;
        try {
            result = restTemplate.getForObject(transUrl(url, uriVariables), responseType, uriVariables);
        } catch (RestClientException e) {
            logger.error("getForObject error:{}", e);
            throw new ServiceException(ErrorCodeEnum.UNKNOWN);
        }
        logger.info("getForObject end:result={}", JSONObject.toJSONString(result));
        return result;
    }

    private String transUrl(String url, Map<String, ?> uriVariables) {
        StringBuilder finalUrl = new StringBuilder(url);
        if (Objects.isNull(uriVariables)) {
            return finalUrl.toString();
        }
        if (StringUtils.isNotBlank(url)) {
            if (url.contains("?")) {
                if (url.endsWith("?")) {
                    finalUrl.append("1=1");
                }
            } else {
                finalUrl.append("?1=1");
            }
            uriVariables.forEach((k, v) -> {
                finalUrl.append("&").append(k).append("={").append(k).append("}");
            });

        }
        return finalUrl.toString();
    }


    public <T> T postForObject(String url, Object request, Class<T> responseType) {
        logger.info("postForObject start:url={},request={},responseType={}",
            url, JSONObject.toJSONString(request), responseType.getName());
        T result = null;
        try {
            result = restTemplate.postForObject(url, getHttpEntity(request, null), responseType);
        } catch (RestClientException e) {
            logger.error("postForObject error:{}", e);
            throw e;
        }
        logger.info("postForObject end:result={}", JSONObject.toJSONString(result));
        return result;
    }

    public <T> T postForObject(String url, Object request, String tenantAccessToken, Class<T> responseType) {
        logger.info("postForObject start:url={},request={},responseType={}, tenantAccessToken:{}",
                url, JSONObject.toJSONString(request), responseType.getName(), tenantAccessToken);
        T result = null;
        try {
            result = restTemplate.postForObject(url, getHttpEntity(request, tenantAccessToken), responseType);
        } catch (RestClientException e) {
            logger.error("postForObject error:{}", JSONObject.toJSONString(result));
            logger.error("postForObject error:{}", e);
            throw e;
        }
        logger.info("postForObject end:result={}", JSONObject.toJSONString(result));
        return result;
    }

    public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, String> headMap) {
        logger.info("postForObject start:url={},request={},responseType={}, tenantAccessToken:{}", url, JSONObject.toJSONString(request), responseType.getName(), JSONObject.toJSONString(headMap));
        T result = null;
        try {
            MultiValueMap<String, String> headers = new LinkedMultiValueMap();
            headMap.forEach((k, v)->{
                headers.add(k, v);
            });
            headers.add("Content-Type", "application/json");
            HttpEntity httpEntity = new HttpEntity(request, headers);
            result = restTemplate.postForObject(url, httpEntity, responseType);
        } catch (RestClientException e) {
            logger.error("postForObject error:{}", JSONObject.toJSONString(result));
            logger.error("postForObject error:{}", e);
            throw e;
        }
        logger.info("postForObject end:result={}", JSONObject.toJSONString(result));
        return result;
    }


    /**
     * 获取Headers
     *
     * @param tenantAccessToken
     * @return
     */
    private MultiValueMap<String, String> getHeaders(String tenantAccessToken) {
        MultiValueMap<String, String> headers = new LinkedMultiValueMap();
        if (StringUtils.isNotEmpty(tenantAccessToken)) {
            headers.add("Authorization", "Bearer " + tenantAccessToken);
        }
        headers.add("Content-Type", "application/json");
        return headers;
    }

    public <T> ResponseEntity<T> exchange(String uri, HttpMethod httpMethod, HttpEntity httpEntity,
                                          Class<T> responseEntity, Map<String, ?> params) {
        List<String> queryStrings = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(params)) {
            params.forEach((k, v) -> {
                queryStrings.add(k + "=" + v);
            });
        }
        String queryString = null;
        if (!CollectionUtils.isEmpty(queryStrings)) {
            queryString = String.join("&", queryStrings);
        }
        if (StringUtils.isNotBlank(queryString)) {
            uri = uri + "?" + queryString;
        }
        ResponseEntity<T> exchange = null;
        logger.info("start exchange=uri:{},httpMethod:{},httpEntity:{},responseEntity:{},params:{}", uri,
                httpMethod.toString(), httpEntity.toString(), responseEntity.toString(),
                Objects.nonNull(params) ? params.toString() : null);
        try {
            exchange = restTemplate.exchange(uri, httpMethod, httpEntity, responseEntity);
        } catch (HttpStatusCodeException e) {
            logger.error("exchange err, resp={}", e.getResponseBodyAsString());
        } catch (Exception e) {
            logger.error("exchange err:", e);
        }
        logger.info("end exchange=exchange:{},", exchange);
        return exchange;
    }

}
