package com.ruoyi.system.service.impl;


import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.system.domain.wx.*;
import com.ruoyi.system.service.IWxPayService;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.omg.CORBA.TIMEOUT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import utils.AppletDecryptDataUtil;
import utils.HttpClientUtils;
import utils.WxPayUtil;

import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.wechat.pay.java.core.http.Constant.*;

/**
 * @Author: tjf
 * @Date: 2024/1/17 14:16
 * @Describe:
 */
@Service
public class WxPayServiceImpl implements IWxPayService {

    private final String JSCODE_SESSION_API = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code";
    //获取微信AccessToken接口地址实例勿改
    private static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    //文本内容安全识别
    private static final String MSGSECCHECK = "https://api.weixin.qq.com/wxa/msg_sec_check?access_token=ACCESS_TOKEN";


    /**
     * 小程序appId
     */
    @Value("${wx.appId}")
    private String appId;

    /**
     * 小程序密钥
     */
    @Value("${wx.appSecret}")
    private String appSecret;

    @Resource
    private WxPayV3Bean wxPayV3Bean;

    @Resource
    private RedisCache redisCache;

    /**
     * 创建微信预支付订单
     *
     * @param req
     * @return
     */
    @Override
    public AjaxResult createOrder(WxPayOrderReqVo req) throws Exception {
        try {
            // 使用自动更新平台证书的RSA配置
            // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
            Config config =
                    new RSAAutoCertificateConfig.Builder()
                            .merchantId(wxPayV3Bean.getMchId())
                            .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                            .merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
                            .apiV3Key(wxPayV3Bean.getApiKey())
                            .build();
            // 构建service
            JsapiService service = new JsapiService.Builder().config(config).build();
            // request.setXxx(val)设置所需参数，具体参数可见Request定义
            PrepayRequest request = new PrepayRequest();
            Amount amount = new Amount();
            //元转分
            amount.setTotal(req.getTotalPrice() * 100);
            request.setAmount(amount);
            request.setAppid(wxPayV3Bean.getAppId());
            request.setMchid(wxPayV3Bean.getMchId());
            request.setDescription(req.getGoodsName());
            request.setNotifyUrl(wxPayV3Bean.getNotifyUrl());
            request.setOutTradeNo("DMGT" + System.currentTimeMillis());
            request.setAttach(req.getOrderType());
            Payer payer = new Payer();
            payer.setOpenid(req.getOpenId());
            request.setPayer(payer);
            // 调用下单方法，得到应答
            PrepayResponse response = service.prepay(request);
            WxPayRespVo vo = new WxPayRespVo();
            Long timeStamp = System.currentTimeMillis() / 1000;
            vo.setTimeStamp(timeStamp);
            String substring = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
            vo.setNonceStr(substring);
            String signatureStr = Stream.of(wxPayV3Bean.getAppId(), String.valueOf(timeStamp), substring, "prepay_id=" + response.getPrepayId())
                    .collect(Collectors.joining("\n", "", "\n"));
            String sign = WxPayUtil.getSign(signatureStr, wxPayV3Bean.getKeyPath());
            vo.setPaySign(sign);
            vo.setPrepayId(response.getPrepayId());
            //todo 存储预支付订单信息
            return AjaxResult.success(vo);
        } catch (ServiceException e) {
            System.out.println("创建微信预支付订单失败");

        }
        return AjaxResult.error();
    }

    /**
     * 微信支付回调
     *
     * @param request
     * @return
     */
    @Override
    public void payNotify(HttpServletRequest request) {
        /**
         * {
         *     "id": "EV-2018022511223320873",
         *     "create_time": "2015-05-20T13:29:35+08:00",
         *     "resource_type": "encrypt-resource",
         *     "event_type": "TRANSACTION.SUCCESS",
         *     "summary": "支付成功",
         *     "resource": {
         *         "original_type": "transaction",
         *         "algorithm": "AEAD_AES_256_GCM",
         *         "ciphertext": "",
         *         "associated_data": "",
         *         "nonce": ""
         *     }
         * }
         */
        try {
            //读取请求体的信息
            ServletInputStream inputStream = request.getInputStream();
            StringBuffer stringBuffer = new StringBuffer();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String s;
            //读取回调请求体
            while ((s = bufferedReader.readLine()) != null) {
                stringBuffer.append(s);
            }
            String s1 = stringBuffer.toString();
            String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
            String nonce = request.getHeader(WECHAT_PAY_NONCE);
            String signType = request.getHeader("Wechatpay-Signature-Type");
            String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
            // 如果已经初始化了 RSAAutoCertificateConfig，可直接使用
            // 没有的话，则构造一个
            // log.error(com.alibaba.fastjson2.JSON.toJSONString(wxPayV3Bean));
            NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                    .merchantId(wxPayV3Bean.getMchId())
                    .privateKeyFromPath(wxPayV3Bean.getKeyPath())
                    .merchantSerialNumber(wxPayV3Bean.getMchSerialNo())
                    .apiV3Key(wxPayV3Bean.getApiKey())
                    .build();
            // 初始化 NotificationParser
            NotificationParser parser = new NotificationParser(config);
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serialNo)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    // 若未设置signType，默认值为 WECHATPAY2-SHA256-RSA2048
                    .signType(signType)
                    .body(s1)
                    .build();
            Transaction parse = parser.parse(requestParam, Transaction.class);
            //todo 存储支付结果
            System.out.println("parse = " + parse);
        } catch (Exception e) {
            System.out.println("获取微信支付回调失败");
        }
    }

    /**
     * 根据code获取小程序openid和unionid
     *
     * @param form
     * @return
     */
    @Override
    public AppletSessionDTO jscode2Session(AppletLoginForm form) {
        // 获取openId和sessionKey
        JSONObject result;
        try {
            String requestUrl = JSCODE_SESSION_API.replace("APPID", appId)
                    .replace("SECRET", appSecret)
                    .replace("JSCODE", form.getCode().trim());

            String jsonStr = HttpClientUtils.doGet(requestUrl);
            result = JSONObject.parseObject(jsonStr);
            if (StringUtils.isEmpty(result.toString())) {
                throw new RuntimeException("错误");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("错误");
        }

        int errcode = result.getIntValue("errcode");
        if (errcode != 0) {
            String errmsg = result.getString("errmsg");
            throw new RuntimeException("获取小程序授权错误信息, " + errmsg);
        }
        // 获取openId,unionId,sessionKey
        AppletSessionDTO appletSession = new AppletSessionDTO();
        appletSession.setOpenId(result.getString("openid"));
        // unionId有可能是空
        appletSession.setUnionId(result.getString("unionid"));
        appletSession.setSessionKey(result.getString("session_key"));
        String phoneNumber = getPhoneNumber(form, appletSession);
        appletSession.setPhoneNumber(phoneNumber);
        return appletSession;
    }

    /**
     * 根获取微信文本内容安全识别
     * @return
     */
    @Override
    public AjaxResult msgSecCheck(AppletSessionDTO dto) {
        Object accessToken =null;
        try {
            //从redis中获取
             accessToken = redisCache.getCacheObject(Constants.WX_ACCESS_TOKEN);
            if (ObjectUtils.isEmpty(accessToken)){
                String result = HttpClientUtils.doGet(ACCESS_TOKEN.replace("APPID", appId)
                        .replace("SECRET", appSecret)
                );
                JSONObject jsonObject = JSONObject.parseObject(result);
                accessToken = jsonObject.get("access_token");
                redisCache.setCacheObject(Constants.WX_ACCESS_TOKEN,accessToken,Constants.CAPTCHA_EXPIRATION, TimeUnit.HOURS);
            }
            Map<String,String> map = new HashMap(4);
            map.put("content",dto.getContent());
            map.put("version","2");
            map.put("openid",dto.getOpenId());
            map.put("scene","2");
            String msgsecCheck = HttpClientUtils.doPost(MSGSECCHECK.replace("ACCESS_TOKEN", (String) accessToken), map);
            JSONObject msgsecCheckResult = JSONObject.parseObject(msgsecCheck);
            Object result = msgsecCheckResult.get("result");
            Object label = JSONObject.parseObject((String) result).get("label");
            if (!"100".equals(label)){
                return AjaxResult.error("含有违规信息");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return AjaxResult.success();
    }

    /**
     * 手机号解密
     */
    private static String getPhoneNumber(AppletLoginForm form, AppletSessionDTO appletSession) {

        // 解密文件
        String encryptedData = form.getEncryptedData();
        // 解密向量
        String iv = form.getIv();
        // 加密秘钥
        byte[] dataByte = Base64.decode(encryptedData);
        // session_key
        byte[] keyByte = Base64.decode(appletSession.getSessionKey());
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        JSONObject result;
        try {
            result = AppletDecryptDataUtil.decryptData(keyByte, ivByte, dataByte);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

        assert result != null;
        String purePhoneNumber = result.getString("purePhoneNumber");
        if (null == purePhoneNumber || purePhoneNumber.isEmpty()) {
            throw new RuntimeException("获取手机号失败");
        }
        return purePhoneNumber;
    }
}
