package org.springblade.yeePay.service;

import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yeepay.yop.sdk.exception.YopClientException;
import com.yeepay.yop.sdk.security.DigestAlgEnum;
import com.yeepay.yop.sdk.service.common.YopClient;
import com.yeepay.yop.sdk.service.common.YopClientBuilder;
import com.yeepay.yop.sdk.service.common.request.YopRequest;
import com.yeepay.yop.sdk.service.common.response.YopResponse;
import com.yeepay.yop.sdk.utils.FileUtils;
import com.yeepay.yop.sdk.utils.Sm2CertUtils;
import com.yeepay.yop.sdk.utils.Sm2Utils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.springblade.core.tool.api.R;
import org.springblade.wx.config.WXConfiger;
import org.springblade.yeePay.common.YeePayConst;
import org.springblade.yeePay.entity.*;
import org.springblade.yeePay.event.BatchTransferEvent;
import org.springblade.yeePay.event.GatewayEvent;
import org.springblade.yeePay.event.PayNotifyEvent;
import org.springblade.yeePay.event.TransferEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;

@Service
@Slf4j
public class YeePayService {
	@Autowired
	WXConfiger wxConfiger;
	@Autowired
	YeePayConst yeePayConst;
	@Autowired
	private ApplicationEventPublisher eventPublisher;

	YopClient yopClient = YopClientBuilder.builder().build();

	/**
	 *	易宝 公众号配置
	 * @param configAppDto
	 * @return*/
	public R configApp(ConfigAppDto configAppDto) {
		YopRequest request = new YopRequest(yeePayConst.getWeChatConfigAddUrl(), "POST");
		addMerchantNo(request);
		if(configAppDto.getAppIdList()!=null){
			request.addParameter("appIdList", JSON.toJSONString(configAppDto.getAppIdList()));
		}
		if(configAppDto.getTradeAuthDirList()!=null){
			request.addParameter("tradeAuthDirList",JSON.toJSONString(configAppDto.getTradeAuthDirList()));
		}
		try {
			YopResponse response = yopClient.request(request);
			return R.data(JSON.parseObject(response.getStringResult()));
		} catch (YopClientException e) {
			e.printStackTrace();
		}
		return R.fail("配置失败");
	}

	/**
	 *	易宝 查询公众号配置
	 *
	 * @param appIdType
	 * @return*/
	public R queryApp(String appIdType) {
		YopRequest request = new YopRequest(yeePayConst.getWeChatConfigQueryUrl(), "GET");
		if(appIdType!=null){
			request.addParameter("appIdType",appIdType);
		}
		addMerchantNo(request);
		try {
			YopResponse response = yopClient.request(request);
			return R.data(JSON.parseObject(response.getStringResult()));
		} catch (YopClientException e) {
			e.printStackTrace();
		}
		return R.fail("查询失败");
	}

	/**
	 * 	易宝 统一下单
	 *
	 * @param initOrderDto
	 * @return*/
	public R initOrder(InitOrderDto initOrderDto){
		YopRequest request = new YopRequest(yeePayConst.getPrePayUrl(), "POST");
//		initOrderDto.setNotifyUrl(yeePayConst.getPayNotify());
		addMerchantNo(request);
		addParamsFromObj(initOrderDto,request,initOrderDto.getClass());
		return execute(request);
	}


	/**
	 * 	添加请求参数
	 * @param obj 请求所需DTO对象
	 * @param request 所需添加参数请求
	 * @param clazz */
	private void addParamsFromObj(Object obj, YopRequest request, Class clazz){
		Arrays.stream(clazz.getDeclaredFields())
			.forEach(field -> {
					field.setAccessible(true);
					try {
						String paramValue = Optional.ofNullable(field.get(obj)).orElse("").toString();
						if(paramValue!=null&!"".equals(paramValue)){
							request.addParameter(field.getName(),paramValue);
						}
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				}
			);
	}

	/**
	 * 	解析解密支付通知数据
	 * @param response 加密数据
	 * */
	public JSONObject deCodeNotifyData(Map<String, Object> response) {
		log.info("回调数据："+JSON.toJSONString(response));
//		Map<String,String> params = notifyParams(response.get("params").toString());

		try {
			String decryptData = Sm4GcmUtils.decrypt_GCM_NoPadding(
				yeePayConst.getSm4Key(),
				response.get("cipherText")+"",
				"",
				""
			);
			log.info("通知解密数据："+ decryptData);
			return JSON.parseObject(decryptData);
		} catch (Exception e) {
			log.error("回调解析错误： "+ e.getMessage());
		}
		return null;
	}

	/**
	 * 	解析解密支付通知数据
	 * @param cipherText 加密数据
	 * */
	public JSONObject deCodeNotifyData(String cipherText) {
		try {
			String decryptData = Sm4GcmUtils.decrypt_GCM_NoPadding(
				yeePayConst.getSm4Key(),
				cipherText,
				"",
				""
			);
//			log.info("通知解密数据："+ decryptData);
			return JSON.parseObject(decryptData);
		} catch (Exception e) {
			log.error("回调解析错误： "+ e.getMessage());
		}
		return null;
	}


	/**
	 *	代付：转账到银行卡(单笔出款)
	 * @param transferDto 转账信息
	 * */
	public R transferToCard(TransferDto transferDto) {
		YopRequest request = new YopRequest(yeePayConst.getTransferOrderUrl(), "POST");
		transferDto.setNotifyUrl(yeePayConst.getTransferNotify());
		addMerchantNo(request);
//		request.addParameter("requestNo", IdUtil.simpleUUID());
		addParamsFromObj(transferDto,request, transferDto.getClass());

		return execute(request);
	}

	/**
	 *	代付：转账到银行卡(批量出款)
	 * @param batchTransferDto 转账信息
	 * */
	public R batchTransferToCard(BatchTransferDto batchTransferDto) {
		YopRequest request = new YopRequest(yeePayConst.getBatchTransferUrl(), "GET");
		batchTransferDto.setNotifyUrl(yeePayConst.getBatchTransferNotify());
		batchTransferDto.setParentMerchantNo(yeePayConst.getParentMerchantNo());
		batchTransferDto.setMerchantNo(yeePayConst.getMerchantNo());
//		RequestConfig requestConfig = request.getRequestConfig();
		request.setContent(JSONObject.toJSONString(batchTransferDto));
		return execute(request);
	}

	private R execute(YopRequest request){
		YopResponse response = null;
		try {
			response = yopClient.request(request);
			return R.data(JSON.parseObject(response.getStringResult()));
		} catch (YopClientException e) {
			e.printStackTrace();
		}
		return R.fail("操作失败");
	}

	/**
	 * 	转账订单查询
	 * @param requestNo 易宝订单号
	 * */
	public R queryTransferOrder( String requestNo ){
		YopRequest request = new YopRequest(yeePayConst.getQueryTransferOrderUrl(), "GET");
		addMerchantNo(request);
		request.addParameter("requestNo", requestNo);
		return execute(request);
	}


	private void addMerchantNo(YopRequest request){
		request.addParameter("parentMerchantNo",yeePayConst.getParentMerchantNo());
		request.addParameter("merchantNo",yeePayConst.getMerchantNo());
	}

	public void orderNotify(Map<String, Object> params) {
		JSONObject decryptData = deCodeNotifyData(params);
		PayNotifyEvent payNotifyEvent = new PayNotifyEvent(decryptData);
		eventPublisher.publishEvent(payNotifyEvent);
	}


	public void transferNotify(Map<String, Object> params) {
		log.info("======================== 转账原始数据 =======================");
		log.info(JSON.toJSONString(params));
		JSONObject decryptData = deCodeNotifyData(params);
		log.info("======================== 转账回调解密数据 =======================");
		log.info(decryptData.toJSONString());
		TransferEvent transferEvent = new TransferEvent(decryptData);
		eventPublisher.publishEvent(transferEvent);
	}


	/**
	 * 	查询账户余额
	 * */
	public R queryBalance() {
		YopRequest request = new YopRequest(yeePayConst.getQueryBalanceUrl(), "GET");
		request.addParameter("merchantNo", yeePayConst.getMerchantNo());
		return execute(request);
	}

	/**
	 * 	生成订单聚合码
	 * @param createCodeDto
	 * */
	public R createCode(CreateCodeDto createCodeDto) {
		YopRequest request = new YopRequest(yeePayConst.getCreateCodeUrl(), "POST");
		addMerchantNo(request);
		addParamsFromObj(createCodeDto,request,createCodeDto.getClass());
		return execute(request);
	}



	/**
	 * 	网关--充值
	 * @param payGateWayDto 网关入参实体
	 * */
	public R payByGateWay(PayGateWayDto payGateWayDto) {
		YopRequest request = new YopRequest(yeePayConst.getPayGateWayUrl(), "POST");
		payGateWayDto.setNotifyUrl(yeePayConst.getGatewayNotify());
		addMerchantNo(request);
		addParamsFromObj(payGateWayDto,request,payGateWayDto.getClass());
		YopResponse response = null;
		String token = null;
		try {
			response = yopClient.request(request);
			token= JSON.parseObject(response.getStringResult()).getString("token");
		} catch (YopClientException e) {
			e.printStackTrace();
			return R.fail("付款失败！");
		}
		Assert.notNull(token,"获取订单token失败，下单异常");
		List<KeyAndValue> params = new ArrayList<>();
		String timeStr = System.currentTimeMillis() / 1000 + "";
		params.add(KeyAndValue.builder().key("appKey").value(yeePayConst.getAppKey()).build());
		params.add(KeyAndValue.builder().key("merchantNo").value(yeePayConst.getMerchantNo()).build());
		params.add(KeyAndValue.builder().key("token").value(token).build());
		params.add(KeyAndValue.builder().key("timestamp").value(timeStr).build());
		params.add(KeyAndValue.builder().key("directPayType").value("").build());
		params.add(KeyAndValue.builder().key("cardType").value("").build());
		params.add(KeyAndValue.builder().key("userNo").value("").build());
		params.add(KeyAndValue.builder().key("userType").value("").build());
		params.add(KeyAndValue.builder().key("ext").value("").build());

		String payUrl = "https://cash.yeepay.com/cashier/std?" +
			"appKey="+yeePayConst.getAppKey()+
			"&merchantNo=" + yeePayConst.getMerchantNo()+
			"&token=" +token+
			"&timestamp=" +timeStr+
			"&directPayType=" +
			"&cardType=" +
			"&userNo=" +
			"&userType=" +
			"&ext=" +
			"&sign="+this.sign(params);
		return R.data(JSONObject.parseObject("{\n" +
			" \"payUrl\": \""+payUrl+"\"\n" +
			"}"));


	}

	public static void main(String[] args) throws CertificateException, NoSuchProviderException {
		X509Certificate x509Certificate = Sm2CertUtils.getX509Certificate(FileUtils.getResourceAsStream("config/certs/4421256739.cer"));
		System.out.println(x509Certificate);
	}
	/**
	 * 网关充值回调
	 * */
	public void gatewayNotify(Map<String, Object> params) {
		log.info("======================== 网关充值原始数据 =======================");
		log.info(JSON.toJSONString(params));
		JSONObject decryptData = deCodeNotifyData(params);
		log.info("======================== 网关充值回调解密数据 =======================");
		log.info(decryptData.toJSONString());
		GatewayEvent gatewayEvent = new GatewayEvent(decryptData);
		eventPublisher.publishEvent(gatewayEvent);
	}

	private	 String sign(List<KeyAndValue> params){
		StringBuilder stringBuilder = new StringBuilder();
		params.stream().forEach(item ->
			stringBuilder.append(item.getKey()+"="+item.getValue()+"&")
		);
		String s = stringBuilder.deleteCharAt(stringBuilder.length() - 1).toString();
		System.out.println(s);
		String priKey = yeePayConst.getPrivateKey();
		PrivateKey privateKey1 = Sm2Utils.string2PrivateKey(priKey);
		String sign = Sm2Utils.sign(s, (BCECPrivateKey) privateKey1)+"$"+ DigestAlgEnum.SM3.getValue();
		return sign;
	}

	public void batchTransferNotify(Map<String, Object> params) {
		log.info("======================== (批量) 转账原始数据 =======================");
		log.info(JSON.toJSONString(params));
		JSONObject decryptData = deCodeNotifyData(params);
		log.info("======================== (批量) 转账回调解密数据 =======================");
		log.info(decryptData.toJSONString());
		BatchTransferEvent event = new BatchTransferEvent(decryptData);
		eventPublisher.publishEvent(event);
	}

	/**
	 * 	扫码支付
	 * @param chargeDto
	 * */
	public R chargePay(ChargeDto chargeDto) {
		YopRequest request = new YopRequest(yeePayConst.getChargeUrl(), "POST");
		addMerchantNo(request);
		request.addParameter("notifyUrl",yeePayConst.getChargeNotify());
		addParamsFromObj(chargeDto,request,chargeDto.getClass());
		return execute(request);
	}


//	@Test
//	public void testSign(){
//		String priKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgHDZ4N8nM1+6wIgJAO6DgytLX000z7w2gbxKReNqO5rmgCgYIKoEcz1UBgi2hRANCAATe2AJOwtbivaFi4/JbV/a3mWku+MicHwGaxA0x1yNvu9ocHOPvVw+kQyd4Ubo5ECaGajs9k3lMUEQZs5c7siHz";
//		String plainText = "a=123";
//		PrivateKey privateKey = Sm2Utils.string2PrivateKey(priKey);
//		String signature = Sm2Utils.sign(plainText, (BCECPrivateKey) privateKey)+"$"+ DigestAlgEnum.SM3.getValue();
//		System.out.println(signature);
//	}
//	@Test
//	public void testVerify(){
//		String pubKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEEJ8l/z6oUWAQ3cNritO5uRjcmgXl28SD8GgOJMU4+rQkhbE5IdsdrSeDslc2yU4UplLL7vHaNnVr0kZpfbc4sA==";
//		String signature = "YOUmkuvtQtcKItql9-IRn4YrITtHZNbjpTbEnxUJLTPKemZ-iIl6kWOfbZEswozdSRtpMkCjryRcuKMrDCGQWg$SM3";
//		String plainText = "merchantNo=10040049388&token=A9111AC520820FE23643FC932A19F0CB8219909B06922FEFECAE1F8601AFE540&timestamp=1625651888&directPayType=&cardType=&userNo=AutoUserNo75773472&userType=PHONE";
//		PublicKey publicKey = Sm2Utils.string2PublicKey(pubKey);
//		boolean verify = Sm2Utils.verifySign(plainText,signature.split("$")[0], (BCECPublicKey) publicKey);
//		Assert.assertTrue(verify);
//	}
}
