package org.springblade.community.pay.wx.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.RefundModel;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import lombok.Cleanup;
import lombok.extern.slf4j.Slf4j;
import org.springblade.community.pay.wx.config.WxPayV3Config;
import org.springblade.community.pay.wx.exception.WxPayException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付工具
 */
@Slf4j
@Component
public class WxPayV3Util {
	/**
	 * 微信支付配置
	 */
	private static WxPayV3Config wxPayConfig;

	/**
	 * serialNo
	 */
	private static String serialNo;

	/**
	 * platSerialNo
	 */
	private static String platSerialNo;

	/**
	 * 初始化
	 * @param wxPayConfig
	 */
	public WxPayV3Util(WxPayV3Config wxPayConfig) {
		WxPayV3Util.wxPayConfig = wxPayConfig;
		try{
			getSerialNumber();
			getPlatSerialNumber();
			v3GetWxPlatformCert();
		}catch (Exception e){
			log.error("读取证书失败:{}",e.getMessage());
		}
	}

	/**
	 * 获取微信支付配置
	 */
	public static WxPayV3Config getWxPayConfig(){
		return wxPayConfig;
	}

	/**
	 * platSerialNo
	 * @return
	 */
	public static String getPlatSerialNumber() {
		if (StrUtil.isEmpty(platSerialNo)) {
			// 获取平台证书序列号
			X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayConfig.getCertPemPath()));
			platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
		}
		System.out.println("platSerialNo:" + platSerialNo);
		return platSerialNo;
	}

	/**
	 * 生成平台证书
	 * @param associatedData
	 * @param nonce
	 * @param cipherText
	 * @param certPath
	 * @return
	 */
	public static String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
		try {
			AesUtil aesUtil = new AesUtil(wxPayConfig.getApiKey3().getBytes(StandardCharsets.UTF_8));
			// 平台证书密文解密
			// encrypt_certificate 中的  associated_data nonce  ciphertext
			String publicKey = aesUtil.decryptToString(
				associatedData.getBytes(StandardCharsets.UTF_8),
				nonce.getBytes(StandardCharsets.UTF_8),
				cipherText
			);
			// 保存证书
			@Cleanup FileWriter writer = new FileWriter(certPath);
			writer.write(publicKey);
			// 获取平台证书序列号
			X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
			return certificate.getSerialNumber().toString(16).toUpperCase();
		} catch (Exception e) {
			e.printStackTrace();
			return e.getMessage();
		}
	}

	/**
	 * 创建微信支付Native模式
	 * @param unifiedOrderModel
	 * @return
	 * @throws WxPayException
	 */
	public static JSON createWxPayOfNative(UnifiedOrderModel unifiedOrderModel) throws WxPayException {
		return createWxPayOfV3(unifiedOrderModel,RequestMethod.POST,WxApiType.NATIVE_PAY);
	}

	/**
	 * 创建微信支付小程序模式
	 * @param unifiedOrderModel
	 * @return
	 * @throws WxPayException
	 */
	public static JSON createWxPayOfJsApi(UnifiedOrderModel unifiedOrderModel) throws WxPayException {
		return createWxPayOfV3(unifiedOrderModel,RequestMethod.POST,WxApiType.JS_API_PAY);
	}

	/**
	 * 微信退款
	 * @param refundModel
	 * @return
	 */
	public static JSON refund(RefundModel refundModel) throws WxPayException {
		return createWxPayOfV3(refundModel,RequestMethod.POST,WxApiType.DOMESTIC_REFUNDS);
	}

	/**
	 * 申请交易账单
	 * @param billDate
	 * @return 交易账单下载地址
	 */
	public JSON tradeBill(String billDate) throws WxPayException {
		Map<String, String> params = new HashMap<>(12);
		params.put("bill_date", billDate);
		params.put("bill_type", "ALL");
		params.put("tar_type", "GZIP");
		return createWxPayOfV3(params,RequestMethod.GET,WxApiType.TRADE_BILL);
	}

	/**
	 * 获取微信平台证书
	 * @return
	 */
	public static JSON v3GetWxPlatformCert() throws WxPayException {
		return createWxPayOfV3(null,RequestMethod.GET,WxApiType.GET_CERTIFICATES);
	}

	/**
	 * 构建UnifiedOrderModel
	 * @return
	 */
	public static UnifiedOrderModel buildV3UnifiedOrderModel(){
		return new UnifiedOrderModel()
			.setAppid(wxPayConfig.getAppId())
			.setMchid(wxPayConfig.getMchId())
			.setOut_trade_no(WxPayKit.generateStr())
			.setNotify_url(wxPayConfig.getNotifyUrl());
	}

	/**
	 * 创建V3支付
	 * @param object
	 * @param requestMethod
	 * @param wxApiType
	 * @return
	 * @throws WxPayException
	 */
	private static JSON createWxPayOfV3(Object object,RequestMethod requestMethod,WxApiType wxApiType) throws WxPayException {
		try {
			log.info("统一下单参数 {}", JSONUtil.toJsonStr(object));
			IJPayHttpResponse response = WxPayApi.v3(
				requestMethod,
				WxDomain.CHINA.toString(),
				wxApiType.toString(),
				wxPayConfig.getMchId(),
				serialNo,
				null,
				wxPayConfig.getKeyPemPath(),
				object!=null?JSONUtil.toJsonStr(object):""
			);
			log.info("统一下单响应 {}", response);
			if(response.getStatus()!=HttpStatus.OK.value()){
				throw new WxPayException("状态异常");
			}

			//获取平台证书
			if(wxApiType==WxApiType.GET_CERTIFICATES){
				String serialNumber = response.getHeader("Wechatpay-Serial");

				String body = response.getBody();

				JSONObject jsonObject = JSONUtil.parseObj(body);
				JSONArray dataArray = jsonObject.getJSONArray("data");
				// 默认认为只有一个平台证书
				JSONObject encryptObject = dataArray.getJSONObject(0);
				JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
				String associatedData = encryptCertificate.getStr("associated_data");
				String cipherText = encryptCertificate.getStr("ciphertext");
				String nonce = encryptCertificate.getStr("nonce");
				String serialNo = encryptObject.getStr("serial_no");
				final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, wxPayConfig.getPlatformCertPath());
				log.info("平台证书序列号: {} serialNo: {}", serialNumber, serialNo);
			}

			// 根据证书序列号查询对应的证书来验证签名结果
			boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getPlatformCertPath());
			log.info("verifySignature: {}", verifySignature);
			if (!verifySignature) {
				throw new WxPayException("签名错误");
			}

			return JSON.parseObject(response.getBody());
		} catch (Exception e) {
			log.error("微信支付错误信息: {}", e.getMessage());
			throw new WxPayException(e);
		}
	}

	/**
	 * serialNo
	 * @return
	 */
	public static String getSerialNumber() {
		if (StrUtil.isEmpty(serialNo)) {
			// 获取证书序列号
			X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayConfig.getCertPemPath()));
			serialNo = certificate.getSerialNumber().toString(16).toUpperCase();

            System.out.println("输出证书信息:\n" + certificate);
            // 输出关键信息，截取部分并进行标记
            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
            System.out.println("版本号:" + certificate.getVersion());
            System.out.println("签发者：" + certificate.getIssuerDN());
            System.out.println("有效起始日期：" + certificate.getNotBefore());
            System.out.println("有效终止日期：" + certificate.getNotAfter());
            System.out.println("主体名：" + certificate.getSubjectDN());
            System.out.println("签名算法：" + certificate.getSigAlgName());
            System.out.println("签名：" + certificate.getSignature().toString());
		}
		System.out.println("serialNo:" + serialNo);
		return serialNo;
	}
}
