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.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethod;
import com.ijpay.core.enums.SignType;
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.ProfitSharingModel;
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.core.log.logger.BladeLogger;
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.*;
import org.springblade.modules.system.entity.UserWeChat;
import org.springblade.modules.system.service.IUserWeChatService;
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.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 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;
	@Resource
	private IUserWeChatService userWeChatService;
	@Lazy
	@Resource
	private IOrderService orderService;
	@Lazy
	private IAfterSalesService afterSalesService;
	@Resource
	private BladeLogger logger;

	@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 void profitSharingAddReceiver(String openId) {
		UserWeChat userWeChat = userWeChatService.lambdaQuery().eq(UserWeChat::getOpenId, openId).one();
		// 检察是否添加过
//		if (YesOrNoEnum.YES.getValue().equals(userWeChat.getIsAddReceiver())) {
//			return;
//		}

		Map<String, String> map = new HashMap<>();
		map.put("sub_mchid", getMchId());
		map.put("appid", wxPayV3Bean.getAppIdxcx());
		map.put("type", "PERSONAL_OPENID");
		map.put("account", openId);
		map.put("relation_type", "USER");

		try {
			IJPayHttpResponse response = WxPayApi.v3(
					RequestMethod.POST,
					WxDomain.CHINA.toString(),
					WxApiType.V3_PROFIT_SHARING_RECEIVERS_ADD.toString(),
					wxPayV3Bean.getMchId(),
					getSerialNumber(),
					null,
					wxPayV3Bean.getKeyPath(),
					JSONUtil.toJsonStr(map)
			);

			JSONObject jsonObject = JSON.parseObject(response.getBody());
			log.info("添加分账接收方返回结果:[{}]", jsonObject);
			// 记录
			userWeChat.setIsAddReceiver(YesOrNoEnum.YES.getValue());
			userWeChatService.updateById(userWeChat);

		} catch (Exception e) {
			log.error("添加分账接收方失败_[{}]", e.getMessage());
		}
	}

	@Override
	public void unfreeze(String transactionId, String outOrderNo) {
		Map<String, String> map = new HashMap<>();
		map.put("mch_id", getMchId());
		map.put("transaction_id", transactionId);
		map.put("out_order_no", outOrderNo);
		map.put("nonce_str", RandomUtil.randomString(32));
		map.put("sign", WxPayKit.createSign(map, wxPayV3Bean.getApiKey3(), SignType.HMACSHA256));
		String execution = WxPayApi.execution(WxPayApi.getReqUrl(WxApiType.PROFIT_SHARING_UNFREEZE), map);
		log.info("解冻剩余资金结果返回:[{}]", execution);
	}

	@Override
	public boolean profitSharingQuery(String transactionId, String outOrderNo) {
		Map<String, String> map = new HashMap<>();
		map.put("mch_id", getMchId());
		map.put("transaction_id", transactionId);
		map.put("out_order_no", outOrderNo);
		map.put("nonce_str", RandomUtil.randomString(32));
		map.put("sign", WxPayKit.createSign(map, wxPayV3Bean.getApiKey3(), SignType.HMACSHA256));
		String query = WxPayApi.profitSharingQuery(map);
		log.info("查询分账结果返回:[{}]", query);
		JSONObject jsonObject = JSON.parseObject(query);
		if ("SUCCESS".equals(jsonObject.get("result_code"))) {
			String status = jsonObject.getString("status");
			return "FINISHED".equals(status);
		} else {
			log.error("查询分账结果失败,err_code:[{}], err_code_des: [{}]", jsonObject.getString("err_code"), jsonObject.getString("err_code_des"));
		}
		return false;
	}

	/**
	 * 分账
	 * @param transactionId 微信交易ID
	 * @param outOrderNo 订单号
	 * @param openId openId
	 * @param amount 分账金额
	 * @param description 分账描述
	 * @return
	 */
	@Override
	public boolean profitSharing(String transactionId, String outOrderNo, String openId, int amount, String description) {
		// 构建请求json参数
		JSONArray array = new JSONArray();
		JSONObject json = new JSONObject();
		json.put("type", "PERSONAL_OPENID");
		json.put("account", openId);
		json.put("amount", amount);
		json.put("description", description);
		array.add(json);

		Map<String, Object> map = new HashMap<>();
		map.put("appid", wxPayV3Bean.getAppIdxcx());
		map.put("transaction_id", transactionId);
		map.put("out_order_no", outOrderNo);
		map.put("unfreeze_unsplit", true);
		map.put("receivers", array);

		try {
			IJPayHttpResponse response = WxPayApi.v3(
					RequestMethod.POST,
					WxDomain.CHINA.toString(),
					WxApiType.V3_PROFIT_SHARING_ORDERS.toString(),
					wxPayV3Bean.getMchId(),
					getSerialNumber(),
					null,
					wxPayV3Bean.getKeyPath(),
					JSONUtil.toJsonStr(map)
			);
			JSONObject result = JSON.parseObject(response.getBody());
			log.info("请求单次分账返回结果:[{}]", result);
			if (StrUtil.isEmpty(result.getString("code"))) {
				return true;
			} else {
				logger.error("微信单次分账异常", "分账订单号：" + outOrderNo + ",微信返回结果：" + result.toJSONString());
				return false;
			}
		} catch (Exception e) {
			log.error("请求单次分账失败,[{}]", e.getMessage());
		}
		return false;
	}

	@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);

	}

	@Override
	public Boolean orderRefund(String orderNo, Long orderId, BigDecimal refundAmount, BigDecimal totalAmount,  Long afterSalesId) {
		try {
			OrderRefund orderRefund = OrderRefund.build(orderId);
			orderRefund.setAfterSalesId(afterSalesId);
			orderRefund.setRefuseAmount(refundAmount);
			RefundModel refundModel = new RefundModel()
					.setOut_refund_no(orderRefund.getRefundNo())
					.setNotify_url(wxPayV3Bean.getDomain().concat(REFUND_NOTIFY_URL))
					.setAmount(new RefundAmount().setRefund((int) (refundAmount.doubleValue() * 100)).setTotal((int) (totalAmount.doubleValue() * 100)).setCurrency("CNY"));
			refundModel.setOut_trade_no(orderNo);
			IJPayHttpResponse response = WxPayApi.v3(
					RequestMethod.POST,
					WxDomain.CHINA.toString(),
					WxApiType.DOMESTIC_REFUNDS.toString(),
					wxPayV3Bean.getMchId(),
					getSerialNumber(),
					null,
					wxPayV3Bean.getKeyPath(),
					JSONUtil.toJsonStr(refundModel)
			);
			log.info("订单号[{}],微信退款响应 {}", orderNo, 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("订单号[{}],微信退款成功", orderNo);
				orderRefundService.success(orderRefund);
				afterSalesService.refund(afterSalesId);
				return true;
			}
			if ("PROCESSING".equals(status)) {
				log.info("订单号[{}],微信退款处理中", orderNo);
				orderRefundService.saveOrUpdate(orderRefund);
			}
			if ("ABNORMAL".equals(status)) {
				log.info("订单号[{}],微信退款失败", orderNo);
				orderRefundService.failure(orderRefund, "退款失败");
				throw new ServiceException("微信退款失败");
			}
			return false;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
			throw new ServiceException("微信退款失败_" + e.getMessage());
		}
	}

	@Override
	public Boolean orderRefund(Order order, Long afterSalesId) {
		return orderRefund(order.getOrderNo(), order.getId(), order.getPaymentAmount(), order.getTotalAmount(), afterSalesId);
	}

	
}
