package com.jsowell.pile.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.jsowell.common.enums.MemberWalletEnum;
import com.jsowell.common.enums.weixin.WeiXinPayTradeStatus;
import com.jsowell.common.enums.ykc.ActionTypeEnum;
import com.jsowell.common.enums.ykc.PayModeEnum;
import com.jsowell.common.enums.ykc.PaymentInstitutionsEnum;
import com.jsowell.common.enums.ykc.ScenarioEnum;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.id.SnowflakeIdWorker;
import com.jsowell.pile.domain.MemberTransactionRecord;
import com.jsowell.pile.domain.WxpayCallbackRecord;
import com.jsowell.pile.domain.WxpayRefundCallback;
import com.jsowell.pile.dto.PaymentScenarioDTO;
import com.jsowell.pile.dto.WeixinPayDTO;
import com.jsowell.pile.service.IMemberBasicInfoService;
import com.jsowell.pile.service.IMemberTransactionRecordService;
import com.jsowell.pile.service.WechatPayService;
import com.jsowell.pile.service.WxpayCallbackRecordService;
import com.jsowell.pile.service.WxpayRefundCallbackService;
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
import com.jsowell.wxpay.common.WeChatPayParameter;
import com.jsowell.wxpay.response.WechatPayNotifyParameter;
import com.jsowell.wxpay.response.WechatPayNotifyResource;
import com.jsowell.wxpay.response.WechatPayRefundNotifyResource;
import com.jsowell.wxpay.response.WechatPayRefundRequest;
import com.jsowell.wxpay.response.WechatPayRefundResponse;
import com.jsowell.wxpay.utils.AesUtil;
import com.jsowell.wxpay.utils.HttpUtils;
import com.jsowell.wxpay.utils.WechatPayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付
 */
@Slf4j
@Service
public class WechatPayServiceImpl implements WechatPayService {

	@Autowired
	private WxpayCallbackRecordService wxpayCallbackRecordService;

	@Autowired
	private WxpayRefundCallbackService wxpayRefundCallbackService;

	@Autowired
	private IMemberBasicInfoService memberBasicInfoService;

	@Autowired
	private IMemberTransactionRecordService memberTransactionRecordService;

	@Override
	public Map<String, Object> weixinPayV3(WeixinPayDTO dto) throws Exception {
		String openId = dto.getOpenId();
		//封装请求参数
		Map<String, Object> paramMap = new HashMap<>();
		// 支付的产品（小程序或者公众号，主要需要和微信支付绑定哦）
		paramMap.put("appid", WeChatPayParameter.appId);
		// 支付的商户号
		paramMap.put("mchid", WeChatPayParameter.mchId);
		// 商品描述
		paramMap.put("description", dto.getDescription());
		// 商户订单号
		paramMap.put("out_trade_no", SnowflakeIdWorker.getSnowflakeId());
		// 交易结束时间
		paramMap.put("time_expire", getTimeExpire());
		// 附加数据
		paramMap.put("attach", dto.getAttach());
		// 通知地址
		paramMap.put("notify_url", WeChatPayParameter.notifyUrl);

		Map<String, Object> amountMap = Maps.newHashMap();
		//订单金额 单位分
		amountMap.put("total", Integer.parseInt(getMoney(dto.getAmount())));
		amountMap.put("currency", "CNY");
		paramMap.put("amount", amountMap);
		// 设置小程序所需的openid
		Map<String, Object> payerMap = Maps.newHashMap();
		payerMap.put("openid", openId);
		paramMap.put("payer", payerMap);

		ObjectMapper objectMapper = new ObjectMapper();
		String body = objectMapper.writeValueAsString(paramMap);
		log.info("支付的相关参数是：{}", body);

		Map<String, Object> stringObjectMap = HttpUtils.doPostWexin(WeChatPayParameter.unifiedOrderUrlJS, body);
		try {
			return WechatPayUtils.getTokenWeixin(WeChatPayParameter.appId, String.valueOf(stringObjectMap.get("prepay_id")));
		} catch (Exception e) {
			log.error("微信支付v3 error", e);
		}
		return null;
	}

	/**
	 * 获取过期时间
	 * @return
	 */
	private String getTimeExpire() {
		long currentTimeMillis = System.currentTimeMillis();
		currentTimeMillis = currentTimeMillis + (30 * 60 * 1000);
		return DateUtils.timeStampToRfc3339(currentTimeMillis);
	}

	/**
	 * 元转换成分
	 *
	 * @param amount
	 * @return
	 */
	public static String getMoney(String amount) {
		if (amount == null) {
			return "";
		}
		// 金额转化为分为单位
		// 处理包含, ￥ 或者$的金额
		String currency = amount.replaceAll("\\$|\\￥|\\,", "");
		int index = currency.indexOf(".");
		int length = currency.length();
		Long amLong = 0L;
		if (index == -1) {
			amLong = Long.valueOf(currency + "00");
		} else if (length - index >= 3) {
			amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));
		} else if (length - index == 2) {
			amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);
		} else {
			amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");
		}
		return amLong.toString();
	}

	public static void main(String[] args) {
		String s = "{\n" +
				"    \"mchid\": \"1632405339\",\n" +
				"    \"appid\": \"wxbb3e0d474569481d\",\n" +
				"    \"out_trade_no\": \"823422832569454592\",\n" +
				"    \"transaction_id\": \"4200001841202305301676160680\",\n" +
				"    \"trade_type\": \"JSAPI\",\n" +
				"    \"trade_state\": \"SUCCESS\",\n" +
				"    \"trade_state_desc\": \"支付成功\",\n" +
				"    \"bank_type\": \"OTHERS\",\n" +
				"    \"attach\": \"{\\\"memberId\\\":\\\"82507801\\\",\\\"orderCode\\\":\\\"C27473779928\\\",\\\"type\\\":\\\"order\\\"}\",\n" +
				"    \"success_time\": \"2023-05-30T15:15:20+08:00\",\n" +
				"    \"payer\": {\n" +
				"        \"openid\": \"o4REX5FPLsLOxM2x9ig2MHuuHPqU\"\n" +
				"    },\n" +
				"    \"amount\": {\n" +
				"        \"total\": 5000,\n" +
				"        \"payer_total\": 5000,\n" +
				"        \"currency\": \"CNY\",\n" +
				"        \"payer_currency\": \"CNY\"\n" +
				"    }\n" +
				"}";
		WechatPayNotifyResource wechatPayNotifyResource = JSON.parseObject(s, WechatPayNotifyResource.class);
		System.out.println(wechatPayNotifyResource);
	}

	/**
	 * 微信支付回调
	 * @param request
	 * @param body
	 * @throws Exception
	 * @return
	 */
	@Override
	public Map<String, Object> wechatPayCallbackInfo(HttpServletRequest request, WechatPayNotifyParameter body) throws Exception {
		Map<String, Object> resultMap = Maps.newHashMap();
		//1：获取微信支付回调的获取签名信息
		String timestamp = request.getHeader("Wechatpay-Timestamp");
		String nonce = request.getHeader("Wechatpay-Nonce");
		ObjectMapper objectMapper = new ObjectMapper();
		// 2: 开始解析报文体
		String data = objectMapper.writeValueAsString(body);
		String message = timestamp + "\n" + nonce + "\n" + data + "\n";
		//3：获取应答签名
		String sign = request.getHeader("Wechatpay-Signature");
		//4：获取平台对应的证书
		String serialNo = request.getHeader("Wechatpay-Serial");
		if (!WeChatPayParameter.certificateMap.containsKey(serialNo)) {
			WeChatPayParameter.certificateMap = WechatPayUtils.refreshCertificate();
		}
		X509Certificate x509Certificate = WeChatPayParameter.certificateMap.get(serialNo);
		if (!WechatPayUtils.verify(x509Certificate, message.getBytes(), sign)) {
			throw new IllegalArgumentException("微信支付签名验证失败:" + message);
		}
		// log.info("签名验证成功");
		WechatPayNotifyParameter.Resource resource = body.getResource();
		// 5：回调报文解密
		AesUtil aesUtil = new AesUtil(WeChatPayParameter.v3Key.getBytes());
		// 解密后json字符串
		String decryptToString = aesUtil.decryptToString(
				resource.getAssociated_data().getBytes(),
				resource.getNonce().getBytes(),
				resource.getCiphertext());
		log.info("2-->decryptToString====>{}", decryptToString);

		//6：获取微信支付返回的信息
		WechatPayNotifyResource wechatPayNotifyResource = JSON.parseObject(decryptToString, WechatPayNotifyResource.class);
		//7: 支付状态的判断 如果是success就代表支付成功
		if (StringUtils.equals(wechatPayNotifyResource.getTrade_state(), WeiXinPayTradeStatus.SUCCESS.getValue())) {
			// 8：获取支付的交易单号，流水号，和附属参数
			String out_trade_no = wechatPayNotifyResource.getOut_trade_no();
			// 微信支付单号
			String transaction_id = wechatPayNotifyResource.getTransaction_id();
			String attach = wechatPayNotifyResource.getAttach();
			log.info("3-->微信支付成功,商户订单号是:{}, 支付订单号:{}, 附属参数是：{}", out_trade_no, transaction_id, attach);
			// 转换附属参数
			PaymentScenarioDTO paymentScenarioDTO = JSONObject.parseObject(attach, PaymentScenarioDTO.class);
			String type = paymentScenarioDTO.getType();
			//
			BigDecimal amount = new BigDecimal(wechatPayNotifyResource.getAmount().getTotal());
			if (StringUtils.equals(type, ScenarioEnum.ORDER.getValue())) {
				// 1-订单支付
				resultMap.put("orderCode", paymentScenarioDTO.getOrderCode());
				resultMap.put("memberId", paymentScenarioDTO.getMemberId());
				resultMap.put("amount", amount);
				resultMap.put("type", type);
			} else if (StringUtils.equals(type, ScenarioEnum.BALANCE.getValue())) {
				// 2-充值余额
				resultMap.put("memberId", paymentScenarioDTO.getMemberId());
				resultMap.put("amount", amount);
				resultMap.put("type", type);
			}
			resultMap.put("out_trade_no", out_trade_no);
			resultMap.put("transaction_id", transaction_id);
			// 保存微信支付记录
			WxpayCallbackRecord record = new WxpayCallbackRecord();
			record.setPayScenario(type);
			record.setMemberId(paymentScenarioDTO.getMemberId());
			record.setOrderCode(paymentScenarioDTO.getOrderCode());
			record.setOutTradeNo(out_trade_no);
			record.setTransactionId(transaction_id);
			record.setMchId(wechatPayNotifyResource.getMchid());
			record.setAppId(wechatPayNotifyResource.getAppid());
			record.setTradeType(wechatPayNotifyResource.getTrade_type());
			record.setTradeState(wechatPayNotifyResource.getTrade_state());
			record.setTradeStateDesc(wechatPayNotifyResource.getTrade_state_desc());
			record.setBankType(wechatPayNotifyResource.getBank_type());
			record.setAttach(wechatPayNotifyResource.getAttach());
			record.setSuccessTime(DateUtils.toLocalDateTime(wechatPayNotifyResource.getSuccess_time(), DateUtils.RFC3339));
			record.setPayerOpenId(wechatPayNotifyResource.getPayer().getOpenid());
			record.setPayerTotal(wechatPayNotifyResource.getAmount().getTotal());
			wxpayCallbackRecordService.insertSelective(record);
		}
		return resultMap;
	}

	/**
	 * 获取微信退款回调信息
	 *
	 * @param request
	 * @param body
	 * @return
	 */
	@Override
	public Map<String, Object> wechatPayRefundCallbackInfo(HttpServletRequest request, WechatPayNotifyParameter body) throws Exception {
		//1：获取微信支付回调的获取签名信息
		String timestamp = request.getHeader("Wechatpay-Timestamp");
		String nonce = request.getHeader("Wechatpay-Nonce");
		ObjectMapper objectMapper = new ObjectMapper();
		// 2: 开始解析报文体
		String data = objectMapper.writeValueAsString(body);
		String message = timestamp + "\n" + nonce + "\n" + data + "\n";
		//3：获取应答签名
		String sign = request.getHeader("Wechatpay-Signature");
		//4：获取平台对应的证书
		String serialNo = request.getHeader("Wechatpay-Serial");
		if (!WeChatPayParameter.certificateMap.containsKey(serialNo)) {
			WeChatPayParameter.certificateMap = WechatPayUtils.refreshCertificate();
		}
		X509Certificate x509Certificate = WeChatPayParameter.certificateMap.get(serialNo);
		if (!WechatPayUtils.verify(x509Certificate, message.getBytes(), sign)) {
			throw new IllegalArgumentException("微信支付签名验证失败:" + message);
		}
		// log.info("签名验证成功");
		WechatPayNotifyParameter.Resource resource = body.getResource();
		// 5：回调报文解密
		AesUtil aesUtil = new AesUtil(WeChatPayParameter.v3Key.getBytes());
		// 解密后json字符串
		String decryptToString = aesUtil.decryptToString(
				resource.getAssociated_data().getBytes(),
				resource.getNonce().getBytes(),
				resource.getCiphertext());
		log.info("微信退款回调信息:{}", decryptToString);

		WechatPayRefundNotifyResource refundNotifyResource = JSONObject.parseObject(decryptToString, WechatPayRefundNotifyResource.class);
		if (refundNotifyResource == null) {
			return null;
		}
		// 查询原支付信息，获取是订单结算退款还是余额退款
		String out_trade_no = refundNotifyResource.getOut_trade_no();
		String out_refund_no = refundNotifyResource.getOut_refund_no();
		String transaction_id = refundNotifyResource.getTransaction_id();
		String refund_id = refundNotifyResource.getRefund_id();

		WxpayCallbackRecord wxpayCallbackRecord = wxpayCallbackRecordService.selectByOutTradeNo(out_trade_no);
		if (wxpayCallbackRecord == null) {
			log.info("查询原支付信息为空  OutTradeNo:{}", out_trade_no);
			return null;
		}

		String memberId = wxpayCallbackRecord.getMemberId();
		String orderCode = wxpayCallbackRecord.getOrderCode();
		// 退款金额 单位分
		int payer_refund = refundNotifyResource.getAmount().getPayer_refund();
		// 分转成元
		BigDecimal refundAmount = new BigDecimal(payer_refund).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
		if (StringUtils.equals(wxpayCallbackRecord.getPayScenario(), ScenarioEnum.BALANCE.getValue())) {
			// 这笔支付订单原来是充值余额的，退款成功了，需要扣掉会员的本金金额
			UpdateMemberBalanceDTO dto = new UpdateMemberBalanceDTO();
			dto.setMemberId(memberId);
			dto.setUpdatePrincipalBalance(refundAmount); // 更新会员本金金额，单位元
			dto.setType(MemberWalletEnum.TYPE_OUT.getValue());
			dto.setSubType(MemberWalletEnum.SUBTYPE_USER_REFUND.getValue());
			memberBasicInfoService.updateMemberBalance(dto);
		}

		// 保存微信退款回调信息
		WxpayRefundCallback record = WxpayRefundCallback.builder()
				.memberId(memberId)
				.orderCode(orderCode)
				.outTradeNo(out_trade_no)
				.outRefundNo(out_refund_no)
				.transactionId(transaction_id)
				.mchId(refundNotifyResource.getMchid())
				.refundId(refund_id)
				.refundStatus(refundNotifyResource.getRefund_status())
				.successTime(refundNotifyResource.getSuccess_time())
				.userReceivedAccount(refundNotifyResource.getUser_received_account())
				.payerTotal(refundNotifyResource.getAmount().getPayer_total() + "")
				.payerRefund(payer_refund + "") // 微信支付接收单位分
				.amountTotal(refundNotifyResource.getAmount().getTotal() + "")
				.amountRefund(refundNotifyResource.getAmount().getRefund() + "")
				.build();
		wxpayRefundCallbackService.insertSelective(record);

		// 记录会员交易流水
		MemberTransactionRecord memberTransactionRecord = MemberTransactionRecord.builder()
				.orderCode(orderCode)
				.scenarioType(wxpayCallbackRecord.getPayScenario())
				.memberId(memberId)
				.actionType(ActionTypeEnum.REVERSE.getValue())
				.payMode(PayModeEnum.PAYMENT_OF_WECHATPAY.getValue())
				.paymentInstitutions(PaymentInstitutionsEnum.WECHAT_PAY.getValue())
				.amount(refundAmount) // 记录会员交易流水，单位元
				.outTradeNo(out_trade_no)
				.transactionId(transaction_id)
				.outRefundNo(out_refund_no)
				.refundId(refund_id)
				.build();
		memberTransactionRecordService.insertSelective(memberTransactionRecord);
		return null;
	}

	/**
	 * 微信支付 申请退款接口
	 */
	@Override
	public WechatPayRefundResponse ApplyForWechatPayRefundV3(WechatPayRefundRequest request) throws JsonProcessingException {
		ObjectMapper objectMapper = new ObjectMapper();
		String body = objectMapper.writeValueAsString(request);
		log.info("申请退款的相关参数是:{}", body);
		Map<String, Object> stringObjectMap = HttpUtils.doPostWexin(WeChatPayParameter.refundJsUrl, body);
		log.info("申请退款的返回参数是:{}", stringObjectMap);
		return JSON.parseObject(JSON.toJSONString(stringObjectMap), WechatPayRefundResponse.class);
	}

}
