package com.ruoyi.common.weixin.utils;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.weixin.config.WeiXinPayConfig;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;

/**
 * 微信V3支付
 * @author Wzp
 */
@Component
public class WeiXinPayV3Utils {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    // 商户证书私钥
    private PrivateKey merchantPrivateKey;

    // 证书
    private Verifier verifier;

    // 请求客户端
    private CloseableHttpClient httpClient;

    @Resource
    private WeiXinPayConfig weiXinPayConfig;

    /**
     * 获取商户证书私钥
     */
    private void setMerchantPrivateKey() {
        // 加载商户私钥
        merchantPrivateKey = PemUtil.loadPrivateKey(IOUtils.toInputStream(weiXinPayConfig.getApiClientKey(), StandardCharsets.UTF_8));
    }

    /**
     * 获取微信证书
     * @throws Exception 异常
     */
    private void setVerifier() throws Exception {
        if (merchantPrivateKey == null) {
            setMerchantPrivateKey();
        }
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(weiXinPayConfig.getMchId(),
                new WechatPay2Credentials(weiXinPayConfig.getMchId(), new PrivateKeySigner(weiXinPayConfig.getMchSerialNum(), merchantPrivateKey)),
                weiXinPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));

        // 从证书管理器中获取verifier
        verifier = certificatesManager.getVerifier(weiXinPayConfig.getMchId());
    }

    /**
     * 创建请求客户端
     * @throws Exception 异常
     */
    private void setHttpClient() throws Exception {
        if (verifier == null) {
            setVerifier();
        }
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(weiXinPayConfig.getMchId(), weiXinPayConfig.getMchSerialNum(), merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        httpClient = builder.build();
    }

    /**
     * 发送POST请求
     * @param url 请求地址
     * @param params 参数（json）
     * @return 结果
     */
    public JSONObject sendPost(String url, JSONObject params) {
        try {
            if (httpClient == null) {
                setHttpClient();
            }
            HttpPost httpPost = new HttpPost(url);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setEntity(new StringEntity(params.toJSONString(), StandardCharsets.UTF_8));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            logger.info("微信证书 post 请求返回的内容:->{" + bodyAsString+"}");
            if (StringUtils.isEmpty(bodyAsString)) {
                return null;
            }
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            logger.error("微信证书 post 请求异常");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送get请求
     * @param url 请求地址（参数拼接至url）
     * @return 结果
     */
    public JSONObject sendGet(String url) {
        try {
            if (httpClient == null) {
                setHttpClient();
            }
            URIBuilder uriBuilder = new URIBuilder(url);
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
            CloseableHttpResponse response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            logger.info("微信证书 get 请求返回的内容：" + bodyAsString);
            if (StringUtils.isEmpty(bodyAsString)) {
                return null;
            }
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            logger.error("微信证书 get 请求异常");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解析支付回调参数
     * @param request 请求
     * @return 结果
     */
    public JSONObject getNotifyDate(HttpServletRequest request) {

        try {
            // 获取报文
            String body = getRequestBody(request);
            // 随机串
            String nonceStr = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
            // 微信传递过来的签名
            String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
            // 证书序列号（微信平台）
            String serialNo = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);
            // 时间戳
            String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);

            // 构建request，传入必要参数
            NotificationRequest res = new NotificationRequest.Builder().withSerialNumber(serialNo)
                    .withNonce(nonceStr)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
            if (verifier == null){
                setVerifier();
            }
            NotificationHandler handler = new NotificationHandler(verifier, weiXinPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
            Notification notification = handler.parse(res);
            logger.info("回调通知数据：" + notification.toString());

            // 回调参数密文解析
            String decryptData = notification.getDecryptData();
            logger.info("回调解析密文：" + decryptData);
            if (StringUtils.isEmpty(decryptData)) {
                return null;
            }
            return JSONObject.parseObject(decryptData);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密body的密文
     * @param body 请求body参数
     * @return 结果
     */
    private String decryptBody(String body) throws GeneralSecurityException {

        AesUtil aesUtil = new AesUtil(weiXinPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));

        JSONObject object = JSONObject.parseObject(body);
        JSONObject resource = object.getJSONObject("resource");
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");

        return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);

    }

    /**
     * 验证签名
     *
     * @param serialNo  微信平台-证书序列号
     * @param signStr   自己组装的签名串
     * @param signature 微信返回的签名
     * @return 结果
     */
    private boolean verifiedSign(String serialNo, String signStr, String signature) throws Exception {
        if (verifier == null){
            setVerifier();
        }
        return verifier.verify(serialNo, signStr.getBytes(StandardCharsets.UTF_8), signature);
    }

    /**
     * 微信支付v3签名 RSA签名
     * @param message 需要签名的内容
     * @return 结果
     */
    public String signRSA(String message) {
        try {
            if (merchantPrivateKey == null) {
                setMerchantPrivateKey();
            }
            Signer signer = new PrivateKeySigner(weiXinPayConfig.getMchSerialNum(), merchantPrivateKey);
            Signer.SignatureResult signature = signer.sign(message.getBytes(StandardCharsets.UTF_8));
            return signature.getSign();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 读取请求数据流
     * @param request 请求
     * @return 结果
     */
    private String getRequestBody(HttpServletRequest request) {

        StringBuilder sb = new StringBuilder();

        try (ServletInputStream inputStream = request.getInputStream();
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        ) {
            String line;

            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return sb.toString();

    }



}
