package com.yunzhupaas.permission.util.implicit.request;

import com.alibaba.fastjson.JSONObject;
import com.xkcoding.http.support.HttpHeader;
import com.yunzhupaas.constant.MsgCode;
import com.yunzhupaas.permission.util.implicit.ImplicitDefaultSource;
import com.yunzhupaas.permission.util.implicit.ImplicitLoginRequest;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthResponseStatus;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.log.Log;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.utils.HttpUtils;
import me.zhyd.oauth.utils.StringUtils;
import me.zhyd.oauth.utils.UrlBuilder;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.util.Date;

public class ImplicitDingTalkRequest extends AuthDefaultRequest implements ImplicitLoginRequest {
    public ImplicitDingTalkRequest(AuthConfig config) {
        super(config, ImplicitDefaultSource.DINGTALK);
    }

    @Override
    public String getAuthLoginLink() {
        return UrlBuilder.fromBaseUrl(this.source.authorize())
                .queryParam("appid", this.config.getClientId())
                .queryParam("redirect_uri", this.config.getRedirectUri())
                .queryParam("response_type", "code")
                .queryParam("scope", "snsapi_auth")
                .queryParam("state", "STATE")
                .build();
    }

    @Override
    public AuthResponse getUserId(AuthCallback authCallback) {
        try {
            AuthToken authToken = AuthToken.builder()
                    .code(authCallback.getCode()).build();
            AuthUser user = this.getUserByCode(authToken);
            return AuthResponse.builder().code(AuthResponseStatus.SUCCESS.getCode()).data(user).build();
        } catch (Exception var4) {
            Exception e = var4;
            Log.error("Failed to login with oauth authorization.", e);
            return this.responseError(e);
        }
    }

    AuthResponse responseError(Exception e) {
        int errorCode = AuthResponseStatus.FAILURE.getCode();
        String errorMsg = e.getMessage();
        if (e instanceof AuthException) {
            AuthException authException = (AuthException) e;
            errorCode = authException.getErrorCode();
            if (StringUtils.isNotEmpty(authException.getErrorMsg())) {
                errorMsg = authException.getErrorMsg();
            }
        }

        return AuthResponse.builder().code(errorCode).msg(errorMsg).build();
    }

    @Override
    protected AuthToken getAccessToken(AuthCallback authCallback) {
        return null;
    }

    @Override
    protected AuthUser getUserInfo(AuthToken authToken) {
        return null;
    }


    /**
     * 钉钉签名计算
     *
     * @param time
     * @return
     */
    private String getSignature(long time) {
        // 根据timestamp, appSecret计算签名值
        String appSecret = this.config.getClientSecret();
        // 根据timestamp, appSecret计算签名值
        String stringToSign = "" + time;
        Mac mac = null;
        try {
            mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256"));
            byte[] signatureBytes = mac.doFinal(stringToSign.getBytes("UTF-8"));
            String signature = new String(Base64.encodeBase64(signatureBytes));
            if ("".equals(signature)) {
                return "";
            }
            String encoded = URLEncoder.encode(signature, "UTF-8");
            return encoded.replace("+", "%20").replace("*", "%2A").replace("~", "%7E").replace("/", "%2F");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected String doGetUserByCode(AuthToken authToken) {
        long time = new Date().getTime();
        String signature = this.getSignature(time);
        String url = UrlBuilder.fromBaseUrl("https://oapi.dingtalk.com/sns/getuserinfo_bycode")
                .queryParam("accessKey", this.config.getClientId())
                .queryParam("timestamp", time)
                .queryParam("signature", signature)
                .build();
        JSONObject map = new JSONObject();
        map.put("tmp_auth_code", authToken.getCode());
        return (new HttpUtils(this.config.getHttpConfig())).post(url, map.toJSONString(), new HttpHeader().add("Content-Type", "application/json")).getBody();
    }

    protected AuthUser getUserByCode(AuthToken authToken) {
        String response = this.doGetUserByCode(authToken);
        JSONObject object = this.checkResponse(response);
        if (object.get("user_info") != null) {
            JSONObject userInfo = (JSONObject) object.get("user_info");
            AuthToken token = AuthToken.builder().openId(userInfo.getString("openid")).unionId(userInfo.getString("unionid")).build();
            return AuthUser.builder().rawUserInfo(userInfo).uuid(userInfo.getString("unionid")).nickname(userInfo.getString("nick"))
                    .username(object.getString("nick")).gender(AuthUserGender.UNKNOWN).source(this.source.toString()).token(token).build();
        } else {
            throw new AuthException(MsgCode.OA024.get());
        }
    }

    private JSONObject checkResponse(String response) {
        JSONObject object = JSONObject.parseObject(response);
        if (object.containsKey("errcode") && object.getIntValue("errcode") != 0) {
            throw new AuthException(object.getString("errmsg"), this.source);
        } else {
            return object;
        }
    }
}

