package com.jeequan.jeepay.thirdparty.service.wxmp;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.cache.RedisUtil;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.interfaces.wxmp.IWxLiteUrlLinkService;
import com.jeequan.jeepay.core.model.QRCodeParams;
import com.jeequan.jeepay.core.model.context.MchAppConfigContext;
import com.jeequan.jeepay.core.model.oauth2.WxpayOauth2Params;
import com.jeequan.jeepay.core.model.wx.WxAccessToken;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxMpErrorMsgEnum;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/*
* 微信支付 获取小程序 URL Link
*
* @author zx
*
* @date 2021/6/8 17:22
*/
@Service
@Slf4j
public class WxLiteUrlLinkService implements IWxLiteUrlLinkService {

    private static final String GET_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
    private static final String GENERATE_URLLINK_URL = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=%s";
    private static final String GENERATE_SCHEME_URL = "https://api.weixin.qq.com/wxa/generatescheme?access_token=%s";

    // access_token过期值
    public static final List<Integer> ACCESS_TOKEN_ERROR_CODES;
    static {
        ACCESS_TOKEN_ERROR_CODES = Arrays.asList(WxMpErrorMsgEnum.CODE_40001.getCode(), WxMpErrorMsgEnum.CODE_40014.getCode(), WxMpErrorMsgEnum.CODE_42001.getCode());
    }

    @Autowired private ConfigContextQueryService configContextQueryService;

    @Override
    public String generateSchemeLink(MchAppConfigContext mchAppConfigContext, String query, long millisecond) {
        return null;
    }

    @Override
    public String generateUrlLink(MchAppConfigContext mchAppConfigContext, String query, long expiredTime) {

        // oauth2配置参数
        WxpayOauth2Params wxpayOauth2Params;
        if(mchAppConfigContext.isIsvsubMch()){
            // 先获取 特约商户的 小程序 配置信息
            wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryNormalOauth2Params(mchAppConfigContext.getMchApplyment().getIsvNo(), mchAppConfigContext.getMchApp().getAppId(), CS.IF_CODE.WXPAY);

            // 商户未配置 || 使用服务商的小程序时 ：  查询服务商的配置信息
            if(wxpayOauth2Params == null || wxpayOauth2Params.getIsUseSubmchAccount() == null ||  wxpayOauth2Params.getIsUseSubmchAccount() == CS.NO){

                // 查询服务商的配置，  判断取哪个 配置条目。

                // 查询商户 配置 的oauth2配置 ( 支付参数配置 )    selectOauth2InfoId: 配置的oauth2参数
                String ifCodeByMchPassage = configContextQueryService.queryIfCodeByPageType(mchAppConfigContext, QRCodeParams.PAGE_TYPE_WECHAT_LITE);
                String selectOauth2InfoId = configContextQueryService.queryAgentOrIsvSelectOauth2InfoId(mchAppConfigContext.getMchApplyment(), ifCodeByMchPassage);
                wxpayOauth2Params = (WxpayOauth2Params) configContextQueryService.queryIsvOauth2ParamsByAutoSelectOauth2InfoId(mchAppConfigContext.getMchApplyment().getIsvNo(), CS.IF_CODE.WXPAY, selectOauth2InfoId);
            }
        }else{
            wxpayOauth2Params = (WxpayOauth2Params)configContextQueryService.queryNormalOauth2Params(mchAppConfigContext.getMchNo(), mchAppConfigContext.getAppId(), CS.IF_CODE.WXPAY);
        }
        if (wxpayOauth2Params == null || StringUtils.isBlank(wxpayOauth2Params.getLiteAppId()) || StringUtils.isBlank(wxpayOauth2Params.getLiteAppSecret())) {
            throw new BizException("请配置微信oauth2小程序参数");
        }
        // 获取accessToken
        String accessToken = getWxLiteAccessToken(wxpayOauth2Params.getLiteAppId(), wxpayOauth2Params.getLiteAppSecret(), false);
        JSONObject resJSON = getUrlLink(GENERATE_URLLINK_URL,wxpayOauth2Params, accessToken, query, expiredTime);
        // access_token过期
        if (ACCESS_TOKEN_ERROR_CODES.contains(resJSON.getInteger("errcode"))) {
            log.info("\n微信access_token过期，即将重新获取");
            accessToken = getWxLiteAccessToken(wxpayOauth2Params.getLiteAppId(), wxpayOauth2Params.getLiteAppSecret(), true);
            // 尝试重新获取url_link
            resJSON = getUrlLink(GENERATE_URLLINK_URL,wxpayOauth2Params, accessToken, query, expiredTime);
        }
        return resJSON.getString("url_link");
    }

    // 发起获取URL LINK请求
    private JSONObject getUrlLink(String url,WxpayOauth2Params wxpayOauth2Params, String accessToken, String query, long expiredTime) {

        JSONObject reqJSON = new JSONObject();
        if (StringUtils.isNotBlank(wxpayOauth2Params.getLitePagePath())) {
            reqJSON.put("path", wxpayOauth2Params.getLitePagePath()); // 小程序路径
        }
        if (StringUtils.isNotBlank(query)) {
            reqJSON.put("query", query); // 进入小程序时的query
        }
        reqJSON.put("is_expire", true); // true-到期失效
        reqJSON.put("expire_type", 0); // 失效类型，失效时间：0，失效间隔天数：1
        reqJSON.put("expire_time", expiredTime); // 失效时间，为 Unix 时间戳
        if (StringUtils.isNotBlank(wxpayOauth2Params.getLiteEnv())) {
            reqJSON.put("env_version", wxpayOauth2Params.getLiteEnv()); // 小程序版本
        }

        log.info("\n获取微信小程序URL Link，请求地址：{}", String.format(url, accessToken));
        log.info("\n获取微信小程序URL Link，请求参数：{}", reqJSON.toJSONString());
        String result = HttpUtil.createPost(String.format(url, accessToken))
                .body(reqJSON.toJSONString())
                .execute()
                .body();
        log.info("\n获取微信小程序URL Link，响应：{}", result);
        if (StringUtils.isBlank(result)) {
            throw new BizException("获取小程序URL Link失败，返回结果为空");
        }

        JSONObject resJSON = JSONObject.parseObject(result);
        if (resJSON == null || resJSON.getInteger("errcode") == null) {
            throw new BizException("获取小程序URL Link失败，返回结果为空");
        }

        if (resJSON.getInteger("errcode") != 0 && !ACCESS_TOKEN_ERROR_CODES.contains(resJSON.getInteger("errcode"))) {
            throw new BizException(resJSON.getString("errmsg"));
        }

        return resJSON;
    }

    public String getSchemeLink(WxpayOauth2Params wxpayOauth2Params, String accessToken, String query, long expiredTime) {
        JSONObject reqJSON = new JSONObject();
        // true-到期失效
        reqJSON.put("is_expire", true);
        // 失效类型，失效时间：0，失效间隔天数：1
        reqJSON.put("expire_type", 0);
        // 失效时间，为 Unix 时间戳
        reqJSON.put("expire_time", expiredTime);
        JSONObject jump_wxa = new JSONObject();
        jump_wxa.put("path",wxpayOauth2Params.getLitePagePath());
        jump_wxa.put("query",query);
//        jump_wxa.put("env_version", "trial");
        // 小程序版本
        if (StringUtils.isNotBlank(wxpayOauth2Params.getLiteEnv())) {
            jump_wxa.put("env_version", wxpayOauth2Params.getLiteEnv());
        }
        reqJSON.put("jump_wxa",jump_wxa);
        log.info("\n获取微信小程序URL Scheme，请求地址：{}", String.format(GENERATE_SCHEME_URL, accessToken));
        log.info("\n获取微信小程序URL Scheme，请求参数：{}", reqJSON.toJSONString());
        String result = HttpUtil.createPost(String.format(GENERATE_SCHEME_URL, accessToken))
                .body(reqJSON.toJSONString())
                .execute()
                .body();
        log.info("\n获取微信小程序URL Scheme，响应：{}", result);
        if (StringUtils.isBlank(result)) {
            throw new BizException("获取小程序URL Scheme失败，返回结果为空");
        }
        JSONObject resJSON = JSONObject.parseObject(result);
        if (resJSON == null || resJSON.getInteger("errcode") == null) {
            throw new BizException("获取小程序URL Scheme失败，返回结果为空");
        }

        if (resJSON.getInteger("errcode") != 0 && !ACCESS_TOKEN_ERROR_CODES.contains(resJSON.getInteger("errcode"))) {
            throw new BizException(resJSON.getString("errmsg"));
        }
        return resJSON.getString("openlink");
    }

    /**
     * 获取access_token，不要随便刷新token
     * 当接口返回过期时，才需要刷新token
     * @param isRefreshAccessToken  true-刷新token
     * */
    private String getWxLiteAccessToken(String appId, String appSecret, boolean isRefreshAccessToken) {

        // 刷新access_token
        if (isRefreshAccessToken) {
            RedisUtil.del(appId);
        }

        String accessToken = RedisUtil.getString(appId);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        }

        // 获取accessToken
        String url = String.format(GET_ACCESS_TOKEN_URL, appId, appSecret);
        log.info("\n获取微信access_token，请求：{}", url);
        String res = HttpUtil.get(url);
        log.info("\n获取微信access_token，响应：{}", res);
        if (StringUtils.isBlank(res)) {
            throw new BizException("获取accessToken失败");
        }

        WxAccessToken wxAccessToken = JSON.parseObject(res, WxAccessToken.class);
        if (wxAccessToken == null || StringUtils.isBlank(wxAccessToken.getAccessToken()) || wxAccessToken.getExpiresIn() == null) {
            throw new BizException("获取accessToken失败");
        }

        if (wxAccessToken.getExpiresIn() - 100 > 0) {
            RedisUtil.setString(appId, wxAccessToken.getAccessToken(), wxAccessToken.getExpiresIn() - 100, TimeUnit.SECONDS);
        }else {
            RedisUtil.setString(appId, wxAccessToken.getAccessToken(), wxAccessToken.getExpiresIn(), TimeUnit.SECONDS);
        }
        return wxAccessToken.getAccessToken();
    }


}
