package com.sptg.permissions.api.thirdauth.request;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.sptg.permissions.api.thirdauth.constants.CtwingAuthConstants;
import com.sptg.permissions.api.thirdauth.enums.AuthCustomizeSource;
import com.sptg.permissions.api.thirdauth.util.*;
import com.xkcoding.http.util.StringUtil;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;

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

public class AuthCtwingRequest extends AuthDefaultRequest {

    public AuthCtwingRequest(AuthConfig config) {
        super(config, AuthCustomizeSource.CTWING);
    }

    @Override
    public AuthToken getAccessToken(AuthCallback authCallback) {
        String response = this.doPostAuthorizationCode(authCallback.getCode());
        return this.getAuthToken(response);
    }

    @Override
    public AuthUser getUserInfo(AuthToken authToken) {
        String userInfo = this.doGetUserInfo(authToken);
        if(StringUtil.isEmpty(userInfo)){
            throw new AuthException("认证失败");
        }
        JSONObject object = JSONObject.parseObject(userInfo);
        this.checkGetUserInfoResponse(object);
        try {
            String dataStr = CtwingRSAUtils.decryptByPrivateKeyForLongStr(object.getString("data"), CtwingAuthConstants.PRI_KEY);
            JSONObject data = JSONObject.parseObject(dataStr);
            return AuthUser.builder()
                    .uuid(data.getString("openId"))
                    .nickname(data.getString("nickName"))
                    .email(data.getString("email"))
                    .avatar(data.getString("userIconUrl2"))
                    .username(data.getString("mobile"))
                    .token(authToken).source(this.source.toString()).build();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

    @Override
    public String doGetUserInfo(AuthToken authToken) {
        String response = this.userInfoUrl(authToken);
        return response;
    }

    private String getAvatar(JSONObject object) {
        String protrait = object.getString("portrait");
        return StringUtils.isEmpty(protrait) ? null : String.format("http://himg.bdimg.com/sys/portrait/item/%s.jpg", protrait);
    }

    @Override
    public String authorize(String state) {
        // 签名所需参数map
        String paras = getParas(state);
        return UrlBuilder.fromBaseUrl(super.authorize(state))
                .queryParam("appId", CtwingAuthConstants.APP_ID)
                .queryParam("clientType", CtwingAuthConstants.clientType)
                .queryParam("format", CtwingAuthConstants.format)
                .queryParam("version", CtwingAuthConstants.version)
                .queryParam("paras", paras)
                .queryParam("sign", this.getCodeSign(CtwingAuthConstants.APP_ID, CtwingAuthConstants.clientType, CtwingAuthConstants.format, CtwingAuthConstants.version, paras))
                .build();
    }

    @Override
    public String accessTokenUrl(String code){
        return UrlBuilder.fromBaseUrl(super.source.accessToken())
                .queryParam("code", code)
                .queryParam("appId", CtwingAuthConstants.APP_ID)
                .queryParam("client_id", this.config.getClientId())
                .queryParam("client_secret", this.config.getClientSecret())
                .queryParam("grantType", "authorization_login")
                .queryParam("format", "json")
                .queryParam("sign", getAccessTokenSign(CtwingAuthConstants.APP_ID, code, "json", "authorization_login"))
                .build();
    }

    @Override
    public String userInfoUrl(AuthToken authToken) {
        String response = null;
        String timeStamp = String.valueOf(System.currentTimeMillis());
        Map<String, String> generalParamsMap = new HashMap<>(0);
        Map<String, String> businessParamsMap = new HashMap<>(0);
        businessParamsMap.put("accessToken", authToken.getAccessToken());
        try {
            String params = CtwingRequestUtil.generateParams(businessParamsMap, CtwingAuthConstants.APP_SECRET);
            generalParamsMap.put("clientId", CtwingAuthConstants.APP_ID);
            generalParamsMap.put("timeStamp", timeStamp);
            generalParamsMap.put("format", "json");
            generalParamsMap.put("params", params);
            String sign = CtwingRequestUtil.generateSign(generalParamsMap, CtwingAuthConstants.PRI_KEY);
            generalParamsMap.put("sign", sign);
            StringBuffer sb = new StringBuffer();
            for(Map.Entry<String, String> entry : generalParamsMap.entrySet()){
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("clientId", CtwingAuthConstants.APP_ID);
            jsonObject.put("timeStamp", timeStamp);
            jsonObject.put("format", "json");
            jsonObject.put("params", params);
            jsonObject.put("sign", sign);
            System.out.println(CtwingAuthConstants.GET_USER_INFO + "?" + sb.toString());
            Map<String, String> httpHeader = new HashMap<>();
            httpHeader.put("X-APP-KEY", CtwingAuthConstants.X_APP_KEY);
            httpHeader.put("X-APP-ID", CtwingAuthConstants.X_APP_ID);
            HttpRequest postRequest = HttpUtil.createPost(CtwingAuthConstants.GET_USER_INFO);
            postRequest.addHeaders(httpHeader);
            postRequest.form(jsonObject);
            postRequest.timeout(10000);
            postRequest.contentType("application/x-www-form-urlencoded;charset=utf-8");
            response = postRequest.execute().body();
            return response;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private void checkAccessTokenResponse(JSONObject object) {
        Integer result = object.getInteger("result");
        if(result != 10000){
            throw new AuthException(object.getString("msg"));
        }
    }

    private void checkGetUserInfoResponse(JSONObject object) {
        String result = object.getString("result");
        if(!"0".equals(result)){
            throw new AuthException(object.getString("msg"));
        }
    }

    private AuthToken getAuthToken(String response) {
        JSONObject accessTokenObject = JSONObject.parseObject(response);
        this.checkAccessTokenResponse(accessTokenObject);
        return AuthToken.builder().accessToken(accessTokenObject.getString("accessToken")).refreshToken(accessTokenObject.getString("refreshToken")).scope(accessTokenObject.getString("scope")).expireIn(accessTokenObject.getIntValue("rfExpiresIn")).build();
    }

    private String getParas(String state){
        try{
            // 非公共请求参数
            Map<String, String> businessMap = new HashMap<>();
            // 时间戳
            businessMap.put("timeStamp", String.valueOf(System.currentTimeMillis()));
            // 回调地址
            businessMap.put("returnURL", CtwingAuthConstants.returnURL);
            // 页面Key
            businessMap.put("pageKey", CtwingAuthConstants.pageKey);
            // 登录类型
            businessMap.put("regReturnUrl", CtwingAuthConstants.regReturnUrl);
            // 状态参数
            businessMap.put("state", state);

            // 拼接非公共请求参数（无顺序要求）
            StringBuffer sb = new StringBuffer();
            for(Map.Entry<String, String> entry : businessMap.entrySet()) {
                sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }

            // XXTea加密非公共加密参数
            byte[] encValue = XXTeaUtil.encrypt(sb.toString().getBytes(CtwingAuthConstants.DEFAULT_CHARSET)
                    , CtwingAuthConstants.APP_SECRET.getBytes(CtwingAuthConstants.DEFAULT_CHARSET));
            return ByteFormat.bytesToHexString(encValue);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private String getCodeSign(String appId, String clientType, String format, String version, String paras){
        try{
            String signValue = appId + clientType + format + version + paras;

            // HMAC-SHA1签名
            byte[] encValue = HmacSha1Util.getHmacSHA1(signValue, CtwingAuthConstants.APP_SECRET);
            return ByteFormat.bytesToHexString(encValue);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private String getAccessTokenSign(String appId, String code, String format, String grantType){
        try{
            String signValue = appId + code + format + grantType;

            // HMAC-SHA1签名
            byte[] encValue = HmacSha1Util.getHmacSHA1(signValue, CtwingAuthConstants.APP_SECRET);
            return ByteFormat.bytesToHexString(encValue);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

}
