package org.springblade.modules.finance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
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.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxApiType;
import com.ijpay.wxpay.enums.WxDomain;
import com.ijpay.wxpay.model.v3.RefundAmount;
import com.ijpay.wxpay.model.v3.RefundModel;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.enums.OrderStateEnum;
import org.springblade.common.enums.PaymentStateEnum;
import org.springblade.common.enums.YesOrNoEnum;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.modules.finance.entity.Order;
import org.springblade.modules.finance.entity.OrderRefund;
import org.springblade.modules.finance.entity.TransactionFlow;
import org.springblade.modules.finance.pay.entity.WxPayV3Bean;
import org.springblade.modules.finance.service.IOrderRefundService;
import org.springblade.modules.finance.service.IOrderService;
import org.springblade.modules.finance.service.ITransactionFlowService;
import org.springblade.modules.finance.service.IWxPayService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.List;

/**
 * Author pangqijun
 * Date 2022/10/12
 * Description
 */
@Slf4j
@Service
public class IWxPayServiceImpl implements IWxPayService {

	private final static String REFUND_NOTIFY_URL = "/v1/wxPay/refundNotify";

	String serialNo;
	String platSerialNo;

	@Resource
	WxPayV3Bean wxPayV3Bean;
	@Resource
	private IOrderRefundService orderRefundService;
	@Lazy
	@Resource
	private IOrderService orderService;

	@Override
	public String getSerialNumber() {
		if (StrUtil.isEmpty(serialNo)) {
			// 获取证书序列号
			X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getCertPath()));
			serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
			log.info("输出证书信息:" + certificate.toString());
			// 输出关键信息，截取部分并进行标记
			log.info("证书序列号:" + certificate.getSerialNumber().toString(16));
			log.info("版本号:" + certificate.getVersion());
			log.info("签发者：" + certificate.getIssuerDN());
			log.info("有效起始日期：" + certificate.getNotBefore());
			log.info("有效终止日期：" + certificate.getNotAfter());
			log.info("主体名：" + certificate.getSubjectDN());
			log.info("签名算法：" + certificate.getSigAlgName());
			log.info("签名：" + certificate.getSignature().toString());
		}
		log.info("serialNo:" + serialNo);
		return serialNo;
	}

	@Override
	public String getPlatSerialNumber() {
		if (StrUtil.isEmpty(platSerialNo)) {
			// 获取平台证书序列号
			X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Bean.getPlatformCertPath()));
			platSerialNo = certificate.getSerialNumber().toString(16).toUpperCase();
		}
		log.info("platSerialNo:" + platSerialNo);
		return platSerialNo;
	}

	@Override
	public String getMchId() {
		return wxPayV3Bean.getMchId();
	}

	@Override
	public String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
		try {
			AesUtil aesUtil = new AesUtil(wxPayV3Bean.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
			);
			// 保存证书
			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();
		}
	}

	@Resource
	private ITransactionFlowService transactionFlowService;

	@Override
	public void refundByFlow(String trNO, Double amount, Double refundAmount) throws Exception {
//		TransactionFlow one = transactionFlowService.lambdaQuery().eq(TransactionFlow::getTransactionNo, trNO).one();
		OrderRefund orderRefund = OrderRefund.build(111L);
		RefundModel refundModel = new RefundModel()
			.setOut_refund_no(orderRefund.getRefundNo())
			.setNotify_url(wxPayV3Bean.getDomain().concat(REFUND_NOTIFY_URL))
			.setAmount(new RefundAmount().setRefund((int) (amount * 100)).setTotal((int) (refundAmount * 100)).setCurrency("CNY"));
		refundModel.setOut_trade_no(trNO);
		IJPayHttpResponse response = WxPayApi.v3(
			RequestMethod.POST,
			WxDomain.CHINA.toString(),
			WxApiType.DOMESTIC_REFUNDS.toString(),
			wxPayV3Bean.getMchId(),
			getSerialNumber(),
			null,
			wxPayV3Bean.getKeyPath(),
			JSONUtil.toJsonStr(refundModel)
		);
		log.info("流水号[{}],微信退款响应 {}", trNO, response);

	}

	@Transactional(propagation = Propagation.NOT_SUPPORTED)
	@Override
	public Boolean orderRefund(Order order) {
		try {
			OrderRefund orderRefund;
			List<OrderRefund> list = orderRefundService.lambdaQuery().eq(OrderRefund::getOrderId, order.getId()).eq(OrderRefund::getRefundState, 1).list();
			if (CollUtil.isEmpty(list)) {
				orderRefund = OrderRefund.build(order.getId());
			} else {
				orderRefund = list.get(0);
			}
			RefundModel refundModel = new RefundModel()
				.setOut_refund_no(orderRefund.getRefundNo())
				.setNotify_url(wxPayV3Bean.getDomain().concat(REFUND_NOTIFY_URL))
				.setAmount(new RefundAmount().setRefund((int) (order.getPaymentAmount().doubleValue() * 100)).setTotal((int) (order.getPaymentAmount().doubleValue() * 100)).setCurrency("CNY"));
			if (StrUtil.isNotBlank(order.getTransactionNo())) {
				TransactionFlow flow = transactionFlowService.lambdaQuery().eq(TransactionFlow::getTransactionNo, order.getTransactionNo()).one();
				// 未支付
				if (new Integer(2).equals(flow.getStatus())) {
					refundModel.setOut_trade_no(order.getTransactionNo());
					refundModel.setAmount(new RefundAmount().setRefund((int) (order.getPaymentAmount().doubleValue() * 100)).setTotal((int) (flow.getAmount().doubleValue() * 100)).setCurrency("CNY"));
				} else {
					refundModel.setOut_trade_no(order.getOrderNo());
					refundModel.setAmount(new RefundAmount().setRefund((int) (order.getPaymentAmount().doubleValue() * 100)).setTotal((int) (order.getPaymentAmount().doubleValue() * 100)).setCurrency("CNY"));
				}
			} else {
				refundModel.setOut_trade_no(order.getOrderNo());
				refundModel.setAmount(new RefundAmount().setRefund((int) (order.getPaymentAmount().doubleValue() * 100)).setTotal((int) (order.getPaymentAmount().doubleValue() * 100)).setCurrency("CNY"));
			}
			IJPayHttpResponse response = WxPayApi.v3(
				RequestMethod.POST,
				WxDomain.CHINA.toString(),
				WxApiType.DOMESTIC_REFUNDS.toString(),
				wxPayV3Bean.getMchId(),
				getSerialNumber(),
				null,
				wxPayV3Bean.getKeyPath(),
				JSONUtil.toJsonStr(refundModel)
			);
			log.info("订单号[{}],微信退款响应 {}", order.getOrderNo(), response);
			JSONObject result = JSON.parseObject(response.getBody());
			String code = result.getString("code");
			if (StrUtil.isNotBlank(code)) {
				String message = result.getString("message");
				orderRefundService.failure(orderRefund, message);
				throw new ServiceException(message);
			}
			String status = result.getString("status");
			if ("SUCCESS".equals(status)) {
				log.info("订单号[{}],微信退款成功", order.getOrderNo());
				order.setPaymentState(PaymentStateEnum.REFUNDED.getValue());
				order.setOrderState(OrderStateEnum.REFUNDED.getValue());
				orderService.updateById(order);
				orderRefundService.success(orderRefund);
				return true;
			}
			if ("PROCESSING".equals(status)) {
				log.info("订单号[{}],微信退款处理中", order.getOrderNo());
				orderRefundService.saveOrUpdate(orderRefund);
			}
			if ("ABNORMAL".equals(status)) {
				log.info("订单号[{}],微信退款失败", order.getOrderNo());
				orderRefundService.failure(orderRefund, "退款失败");
				throw new ServiceException("微信退款失败");
			}
			return false;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new ServiceException("微信退款失败_" + e.getMessage());
		}
	}


}
