package com.app.service.wx;

import com.app.config.WXMPConfig;
import com.app.constant.RedisKeyConstant;
import com.app.dto.wx.WxAccessTokenDto;
import com.app.dto.wx.WxCode2SessionDto;
import com.app.dto.wx.WxUserInfoDto;
import com.app.util.HttpUtil;
import com.app.util.JacksonUtil;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class WXService {

    @Autowired
    private WXMPConfig wxmpConfig;

    @Autowired
    private HttpUtil httpUtil;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 功能描述：
     *
     * @param code
     * @return
     */
    public WxCode2SessionDto jscode2session(String code) {
        String wxResponse = httpUtil.get("https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={js_code}&grant_type={grant_type}", new HashMap<String, String>() {{
            put("appid", wxmpConfig.getAppId());
            put("secret", wxmpConfig.getAppSecret());
            put("js_code", code);
            put("grant_type", "authorization_code");
        }});

        WxCode2SessionDto dto = JacksonUtil.fromJSON(wxResponse, WxCode2SessionDto.class);
        if (dto.getErrcode() == 0) {
            return dto;
        }

        log.error("微信用户认证失败。errcode: [{}], errmsg: [{}]", dto.getErrcode(), dto.getErrmsg());
        return null;
    }

    /**
     * 功能描述：解密用户敏感数据获取用户信息
     *
     * @param sessionKey    数据进行加密签名的密钥
     * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据
     * @param iv            加密算法的初始向量
     * @return
     */
    public WxUserInfoDto getUserInfo(String sessionKey, String encryptedData, String iv) {
        byte[] keyByte = Base64.decode(sessionKey); // 加密秘钥
        byte[] dataByte = Base64.decode(encryptedData); // 被加密的数据
        byte[] ivByte = Base64.decode(iv); // 偏移量

        try {
            /* 如果密钥不足16位，那么就补足.  这个if 中的内容很重要 */
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }

            /* 初始化 */
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters); // 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                return JacksonUtil.fromJSON(result, WxUserInfoDto.class);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }


    /**
     * 功能描述：获取微信接口调用凭证
     */
    public String getAccessToken() {
        /* 判断redis里面有没有accessToken,有直接获取，没有请求微信获取 */
        if (!StringUtils.isEmpty(stringRedisTemplate.opsForValue().get(RedisKeyConstant.MP_ACCESSTOKEN))) {
            return stringRedisTemplate.opsForValue().get(RedisKeyConstant.MP_ACCESSTOKEN).toString();
        }

        String wxResponse = httpUtil.get("https://api.weixin.qq.com/cgi-bin/token?grant_type={grant_type}&appid={appid}&secret={secret}", new HashMap<String, String>() {{
            put("appid", wxmpConfig.getAppId());
            put("secret", wxmpConfig.getAppSecret());
            put("grant_type", "client_credential");
        }});

        WxAccessTokenDto dto = JacksonUtil.fromJSON(wxResponse, WxAccessTokenDto.class);
        if (StringUtils.isEmpty(dto.getAccessToken())) {
            log.error("微信获取AccessToken失败。");
            return null;
        }

        stringRedisTemplate.opsForValue().set(RedisKeyConstant.MP_ACCESSTOKEN, dto.getAccessToken(), dto.getExpiresIn(), TimeUnit.SECONDS);
        log.info("微信获取AccessToken成功accessToken: [{}]。", dto.getAccessToken());

        return dto.getAccessToken();
    }

}
