package org.jeecg.modules.pay.config;

import com.alibaba.fastjson2.JSONObject;
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.PemUtil;
import lombok.extern.slf4j.Slf4j;
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.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.*;

/**
 * @author wangzhiqiang
 * @Date 2022/12/22
 * @Desc 微信V3支付工具类
 */
@Slf4j
@Component
public class WechatPayV3Utils {

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

    //证书
    private Verifier verifier;

    //请求客户端
    private CloseableHttpClient httpClient;

    /**
     * 获取商户证书私钥
     */
    private void setMerchantPrivateKey() throws Exception {
        ClassPathResource classPathResource = new ClassPathResource("cert/apiclient_key.pem");
        InputStream certStream = classPathResource.getInputStream();
        //读取文件形式加载商户私钥
        merchantPrivateKey = PemUtil.loadPrivateKey(certStream);
        //直接把商户私钥以字符串形式配置
//        merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(WechatConstants.WECHAT_MCH_PRIVATE_KEY.getBytes(StandardCharsets.UTF_8)));
    }

    /**
     * 获取微信证书
     *
     * @throws Exception
     */
    private void setVerifier() throws Exception {
        if (merchantPrivateKey == null) {
            setMerchantPrivateKey();
        }
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(WechatConstants.WECHAT_SP_MCH_ID,
                new WechatPay2Credentials(WechatConstants.WECHAT_SP_MCH_ID, new PrivateKeySigner(WechatConstants.WECHAT_MCH_SERIAL_NUM, merchantPrivateKey)),
                WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号，可继续调用putMerchant添加商户信息

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

    /**
     * 创建请求客户端
     *
     * @throws Exception
     */
    private void setHttpClient() throws Exception {
        if (verifier == null) {
            setVerifier();
        }
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(WechatConstants.WECHAT_SP_MCH_ID, WechatConstants.WECHAT_MCH_SERIAL_NUM, 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());
            log.info("微信返回的内容：" + bodyAsString);
            if (StringUtils.isEmpty(bodyAsString)) {
                return null;
            }
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            log.error("微信支付V3请求失败");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送POST请求
     *
     * @param url    请求地址
     * @param params json参数
     * @return
     */
    public JSONObject profitSharingSendPost(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");
            String WechatPaySerial = verifier.getValidCertificate().getSerialNumber().toString(16).toUpperCase(Locale.ROOT);
            httpPost.addHeader("Wechatpay-Serial", WechatPaySerial);
            httpPost.setEntity(new StringEntity(params.toJSONString(), StandardCharsets.UTF_8));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("微信返回的内容：" + bodyAsString);
            if (StringUtils.isEmpty(bodyAsString)) {
                return null;
            }
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            log.error("微信请求失败");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送get请求
     *
     * @param 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");
            log.info("微信请求地址：" + httpGet.getURI());
            CloseableHttpResponse response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("微信返回的内容：" + bodyAsString);
            if (StringUtils.isEmpty(bodyAsString)) {
                return null;
            }
            return JSONObject.parseObject(bodyAsString);
        } catch (Exception e) {
            log.error("微信支付V3请求失败");
            e.printStackTrace();
            return null;
        }
    }

    /**
     *
     * 遍历集合M,取出全部的参数key，将参数key字典序排列，根据排序后的参数key获取对应的value,将非空的参数key和参数value用"="拼接，多个参数之间用"&"拼接
     *
     * @param param   参数
     * @return
     */
    public static String formatUrlParam(Map<String, String> param) {
        String params = "";
        try {
            List<Map.Entry<String, String>> items = new ArrayList<>(param.entrySet());

            //对所有传入的参数按照字段名从小到大排序
            items.sort(Map.Entry.comparingByKey());

            //构造URL 键值对的形式
            StringBuffer sb = new StringBuffer();
            for (Map.Entry<String, String> item : items) {
                if (org.apache.commons.lang.StringUtils.isNotBlank(item.getKey())) {
                    String key = item.getKey();
                    String val = item.getValue();
                    val = URLEncoder.encode(val, StandardCharsets.UTF_8);
                    // 跳过空值参数
                    if (val != null && !val.trim().isEmpty()) {
                        if (sb.length() > 0) {
                            sb.append("&");
                        }
                        sb.append(key).append("=").append(val);
                    }
                }
            }
            params = sb.toString().trim();
        } catch (Exception e) {
            return "";
        }
        return params;
    }

    /**
     * 回调通知验签与解密
     *
     * @param request
     * @return
     */
    public JSONObject getCallbackData(HttpServletRequest request) {
        try {
            if (verifier == null) {
                setVerifier();
            }
            String wechatPaySerial = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SERIAL);
            String nonce = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_NONCE);
            String timestamp = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_TIMESTAMP);
            String signature = request.getHeader(WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE);
            String body;
            BufferedReader reader = request.getReader();
            String line ;
            StringBuilder inputString = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                inputString.append(line);
            }
            body = inputString.toString();
            log.info("body数据："+body);
            reader.close();
            // 构建request，传入必要参数
            NotificationRequest res = new NotificationRequest.Builder().withSerialNumber(wechatPaySerial)
                    .withNonce(nonce)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(body)
                    .build();
            NotificationHandler handler = new NotificationHandler(verifier, WechatConstants.WECHAT_MCH_SECRET_V3.getBytes(StandardCharsets.UTF_8));
            // 验签和解析请求体
            Notification notification = handler.parse(res);
            log.info("回调通知数据：" + notification.toString());
            // 解析开数据
            String decryptData = notification.getDecryptData();
            log.info("回调解析数据：" + decryptData);
            if (StringUtils.isEmpty(decryptData)) {
                return null;
            }
            return JSONObject.parseObject(decryptData);
        } catch (Exception e) {
            log.error("微信支付V3回调失败");
            e.printStackTrace();
            return null;
        }
    }

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


    /**
     * 计算签名
     *
     * @param message
     * @return
     */
    public String sign(byte[] message) {
        try {
            if (merchantPrivateKey == null) {
                setMerchantPrivateKey();
            }
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(merchantPrivateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
}

