package com.cm.common.component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cm.common.config.properties.OauthClientProperties;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.security.oauth2.client.OAuth2ClientContext;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: OAuthRestTemplateUtil
 * @Description: OAuth的Rest请求工具类
 * @Author: WangGeng
 * @Date: 2019/10/16 10:17 下午
 * @Version: 1.0
 **/
@Component
public class OAuthRestTemplateComponent {

    private static Logger LOG = LoggerFactory.getLogger(OAuthRestTemplateComponent.class);
    @Qualifier("oauth2ClientContext")
    private OAuth2ClientContext oAuth2ClientContext;
    @Autowired
    private OauthClientProperties oauthClientProperties;

    /**
     * GET的FORM表单请求
     *
     * @param url
     * @param params
     * @return
     */
    public String doGetForm(String url, Map<String, Object> params) {
        params = getParams(params);
        HttpHeaders httpHeaders = getFormHeader();
        return getGetResponse(httpHeaders, url, params);
    }

    /**
     * Oauth2客户端执行GET的FORM表单请求
     *
     * @param accessToken
     * @param url
     * @param params
     * @return
     */
    public String doGetFormForOAuth2Client(String accessToken, String url, Map<String, Object> params) {
        params = getParams(params);
        params.put("access_token", accessToken);
        HttpHeaders httpHeaders = getFormHeader();
        return getGetResponse(httpHeaders, url, params);
    }

    /**
     * Oauth2上下文执行GET的FORM表单请求（用户登录后）
     *
     * @param url
     * @param params
     * @return
     */
    public String doGetFormForOAuth2Context(String url, Map<String, Object> params) {
        params = getParams(params);
        refreshOAuth2ContextAccessToken();
        String accessToken = getOAuth2ContextAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            return null;
        }
        params.put("access_token", accessToken);
        HttpHeaders httpHeaders = getFormHeader();
        return getGetResponse(httpHeaders, url, params);
    }

    /**
     * APP执行GET的FORM表单请求
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public String doGetFormForApp(String token, String url, Map<String, Object> params) {
        params = getParams(params);
        HttpHeaders httpHeaders = getFormHeader();
        httpHeaders.add("token", token);
        return getGetResponse(httpHeaders, url, params);
    }

    /**
     * 获取GET的FORM表单请求的结果
     *
     * @param httpHeaders
     * @param url
     * @param params
     * @return
     */
    private String getGetResponse(HttpHeaders httpHeaders, String url, Map<String, Object> params) {
        HttpEntity<String> httpEntity = new HttpEntity<>(null, httpHeaders);
        RestTemplate restTemplate = getRestTemplate();
        try {
            return getResponse(restTemplate.exchange(String.format("%s?%s", url, getQueryParams(params)), HttpMethod.GET, httpEntity, String.class));
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * POST的FORM表单请求
     *
     * @param url
     * @param params
     * @return
     */
    public String doPostForm(String url, Map<String, Object> params) {
        params = getParams(params);
        HttpHeaders httpHeaders = getFormHeader();
        return getPostFormResponse(httpHeaders, url, params);
    }

    /**
     * POST的JSON请求
     *
     * @param url
     * @param params
     * @return
     */
    public String doPostJson(String url, Map<String, Object> params) {
        HttpHeaders httpHeaders = getJsonHeader();
        return getPostJsonResponse(httpHeaders, url, params);
    }

    /**
     * Oauth2客户端执行POST的FORM表单请求
     *
     * @param accessToken
     * @param url
     * @param params
     * @return
     */
    public String doPostFormForOAuth2Client(String accessToken, String url, Map<String, Object> params) {
        params = getParams(params);
        HttpHeaders httpHeaders = getFormHeader();
        return getPostFormResponse(httpHeaders, String.format("%s?access_token=%s", url, accessToken), params);
    }

    /**
     * Oauth2客户端执行POST的JSON表单请求
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public String doPostJsonForOAuth2Client(String accessToken, String url, Map<String, Object> params) {
        HttpHeaders httpHeaders = getJsonHeader();
        return getPostJsonResponse(httpHeaders, String.format("%s?access_token=%s", url, accessToken), params);
    }

    /**
     * Oauth2上下文执行POST的FORM表单请求（用户登录后）
     *
     * @param url
     * @param params
     * @return
     */
    public String doPostFormForOAuth2Context(String url, Map<String, Object> params) {
        params = getParams(params);
        refreshOAuth2ContextAccessToken();
        String accessToken = getOAuth2ContextAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            return null;
        }
        HttpHeaders httpHeaders = getFormHeader();
        return getPostFormResponse(httpHeaders, String.format("%s?access_token=%s", url, accessToken), params);
    }

    /**
     * Oauth2上下文执行POST的JSON请求（用户登录后）
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public String doPostJsonForOAuth2Context(String token, String url, Map<String, Object> params) {
        params = getParams(params);
        refreshOAuth2ContextAccessToken();
        String accessToken = getOAuth2ContextAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            return null;
        }
        HttpHeaders httpHeaders = getJsonHeader();
        return getPostJsonResponse(httpHeaders, String.format("%s?access_token=%s", url, accessToken), params);
    }

    /**
     * APP执行POST的FORM表单请求
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public String doPostFormForApp(String token, String url, Map<String, Object> params) {
        params = getParams(params);
        HttpHeaders httpHeaders = getFormHeader();
        httpHeaders.add("token", token);
        return getPostFormResponse(httpHeaders, url, params);
    }

    /**
     * APP执行POST的JSON请求
     *
     * @param token
     * @param url
     * @param params
     * @return
     */
    public String doPostJsonForApp(String token, String url, Map<String, Object> params) {
        HttpHeaders httpHeaders = getJsonHeader();
        httpHeaders.add("token", token);
        return getPostJsonResponse(httpHeaders, url, params);
    }

    /**
     * 获取POST的FORM表单请求的结果
     *
     * @param httpHeaders
     * @param url
     * @param params
     * @return
     */
    private String getPostFormResponse(HttpHeaders httpHeaders, String url, Map<String, Object> params) {
        MultiValueMap<String, Object> queryParams = new LinkedMultiValueMap<>();
        for (Map.Entry<String, Object> kv : params.entrySet()) {
            queryParams.add(kv.getKey(), kv.getValue());
        }
        HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(queryParams, httpHeaders);
        return getPostResponse(httpEntity, url);
    }

    /**
     * 获取POST的JSON请求的结果
     *
     * @param httpHeaders
     * @param url
     * @param params
     * @return
     */
    private String getPostJsonResponse(HttpHeaders httpHeaders, String url, Map<String, Object> params) {
        HttpEntity<String> httpEntity = new HttpEntity<>(JSONObject.toJSONString(params), httpHeaders);
        return getPostResponse(httpEntity, url);
    }

    /**
     * 获取POST请求结果
     *
     * @param httpEntity
     * @param url
     * @return
     */
    private String getPostResponse(HttpEntity httpEntity, String url) {
        RestTemplate restTemplate = getRestTemplate();
        try {
            return getResponse(restTemplate.postForEntity(url, httpEntity, String.class));
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取参数
     *
     * @param params
     * @return
     */
    private Map<String, Object> getParams(Map<String, Object> params) {
        if (params == null) {
            return new HashMap<>(0);
        }
        return params;
    }

    /**
     * 获取form表单请求header
     *
     * @return
     */
    private HttpHeaders getFormHeader() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        return httpHeaders;
    }

    /**
     * 获取json请求header
     *
     * @return
     */
    private HttpHeaders getJsonHeader() {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
        return httpHeaders;
    }

    /**
     * 获取restTemplate
     *
     * @return
     */
    private RestTemplate getRestTemplate() {
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        simpleClientHttpRequestFactory.setConnectTimeout(20 * 1000);
        simpleClientHttpRequestFactory.setReadTimeout(60 * 1000);
        return new RestTemplate(simpleClientHttpRequestFactory);
    }

    /**
     * 返回参数
     *
     * @param responseEntity
     * @return
     */
    private String getResponse(ResponseEntity<String> responseEntity) {
        LOG.debug(">>>> 请求状态：" + responseEntity.getStatusCodeValue());
        if (HttpStatus.OK.value() == responseEntity.getStatusCodeValue()) {
            return responseEntity.getBody();
        } else if (HttpStatus.UNAUTHORIZED.value() == responseEntity.getStatusCodeValue()) {
            return null;
        }
        return "";
    }

    /**
     * 请求参数
     *
     * @param queryParams
     * @return
     */
    private String getQueryParams(Map<String, Object> queryParams) {
        StringBuilder urlParams = new StringBuilder();
        for (Map.Entry<String, Object> kv : queryParams.entrySet()) {
            if (urlParams.length() > 0) {
                urlParams.append("&");
            }
            urlParams.append(kv.getKey()).append("=").append(kv.getValue());
        }
        return urlParams.toString();
    }

    /**
     * 刷新
     */
    private void refreshOAuth2ContextAccessToken() {
        if (oAuth2ClientContext.getAccessToken().getExpiresIn() < 1800) {
            LOG.debug("accessToken时间小于1800s，刷新token");
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            MultiValueMap<String, Object> queryParams = new LinkedMultiValueMap<>();
            queryParams.add("grant_type", "refresh_token");
            queryParams.add("refresh_token", oAuth2ClientContext.getAccessToken().getRefreshToken().getValue());
            queryParams.add("client_id", oauthClientProperties.getClientId());
            queryParams.add("client_secret", oauthClientProperties.getClientSecret());
            HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(queryParams, httpHeaders);
            RestTemplate restTemplate = getRestTemplate();
            try {
                String result = getResponse(restTemplate.postForEntity(oauthClientProperties.getAccessTokenUri(), httpEntity, String.class));
                if (!StringUtils.isBlank(result)) {
                    Map<String, String> tokenMap = JSON.parseObject(result, Map.class);
                    oAuth2ClientContext.setAccessToken(DefaultOAuth2AccessToken.valueOf(tokenMap));
                }
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 获取accessToken
     *
     * @return
     */
    private String getOAuth2ContextAccessToken() {
        return oAuth2ClientContext.getAccessToken().getValue();
    }

}