package org.springblade.payment.plugin;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yeepay.yop.sdk.service.common.response.YopResponse;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.cache.PaymentCache;
import org.springblade.common.constant.CallbackServeUrl;
import org.springblade.common.enums.OrderType;
import org.springblade.common.enums.PaymentType;
import org.springblade.common.enums.TransferStatus;
import org.springblade.common.enums.WithdrawStatus;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.log.logger.BladeLogger;
import org.springblade.core.tool.api.R;
import org.springblade.ldt.bills.entity.TransferRec;
import org.springblade.ldt.bills.entity.WithdrawRec;
import org.springblade.ldt.bills.service.ITransferRecService;
import org.springblade.ldt.bills.service.IWithdrawRecService;
import org.springblade.ldt.shop.entity.Shop;
import org.springblade.ldt.shop.service.IShopService;
import org.springblade.payment.entity.PayParam;
import org.springblade.payment.entity.SuccessParams;
import org.springblade.payment.event.UserPointTransferWithdrawEvent;
import org.springblade.payment.handle.entity.HandleTransferData;
import org.springblade.payment.service.impl.PaymentService;
import org.springblade.yeePay.common.YeePayConst;
import org.springblade.yeePay.common.YeepayApiConstant;
import org.springblade.yeePay.entity.saas.account.TransferB2bQueryDto;
import org.springblade.yeePay.entity.saas.account.TransferOrderDto;
import org.springblade.yeePay.entity.saas.settlement.SettleSelfSettleApplyDto;
import org.springblade.yeePay.service.YeePayService;
import org.springblade.yeePay.service.YeepaySaasService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.Set;

/**
 * @author July
 * @version 1.0.0
 * @ClassName WithdrawPlugin.java
 * @Description 提现插件
 * @createTime 2021年10月22日 16:26:00
 */
@Component
@Slf4j
@AllArgsConstructor
public class WithdrawPlugin implements Withdraw {

	YeePayService yeePayService;
	PaymentService paymentService;
	YeepaySaasService yeepaySaasService;
	IWithdrawRecService withdrawRecService;
	YeePayConst yeePayConst;
	IShopService shopService;
	ApplicationEventPublisher eventPublisher;
	ITransferRecService transferRecService;
	BladeLogger bladeLogger;

	@Override
	public String callbackUrl(String paymentType) {
		return CallbackServeUrl.CALL_BACK_SERVE_URL + CALLBACK_PREFIX + paymentType;
	}

	@Override
	public String csUrl(String paymentType) {
		return CallbackServeUrl.CALL_BACK_SERVE_URL + CSURL_PREFIX + paymentType;
	}

	@Override
	public void callback(HttpServletRequest request) {

		log.info("易宝网关支付回调： map" + JSON.toJSONString(request.getParameterMap()));

		JSONObject jsonObject = yeePayService.deCodeNotifyData(request.getParameterMap().get("cipherText")[0]);
		log.info("易宝网关支付解密回调密文：" + jsonObject.toJSONString());

		SuccessParams successParams = PaymentCache.getSuccessParams(jsonObject.getString("requestNo"));

		Assert.notNull(successParams, "积分提现订单超时，请重新提交！！");
		successParams.setRes(jsonObject);
		successParams.setStatus(
			"SUCCESS".equals(jsonObject.getString("status")) ?
				SuccessParams.PAY_STATUS_SUCCESS :
				SuccessParams.PAY_STATUS_FAIL);
		//处理付款后业务流程
		paymentService.success(successParams);
	}

	@Override
	public void csUrl(HttpServletRequest request) {
		log.info("易宝网关支付回调： map" + JSON.toJSONString(request.getParameterMap()));

		JSONObject jsonObject = yeePayService.deCodeNotifyData(request.getParameterMap().get("cipherText")[0]);
		log.info("易宝网关支付解密回调密文：" + jsonObject.toJSONString());

		SuccessParams successParams = PaymentCache.getSuccessParams(jsonObject.getString("settleRequestNo"));

		Assert.notNull(successParams, "余额提现订单超时，请重新提交！！");
		successParams.setRes(jsonObject);
		successParams.setStatus(
			"SUCCESS".equals(jsonObject.getString("status")) ?
				SuccessParams.PAY_STATUS_SUCCESS :
				SuccessParams.PAY_STATUS_FAIL);
		//处理付款后业务流程
		paymentService.success(successParams);
	}

	@Override
	public R userWithdraw(PayParam payParam) {
		WithdrawRec withdrawRec = withdrawRecService.getById(payParam.getOrderId());
		Assert.notNull(withdrawRec, "提现申请查询失败，无此申请信息！");

		Shop shop = shopService.getOne(Wrappers.<Shop>lambdaQuery().select(Shop::getMerchantNo).eq(Shop::getId, withdrawRec.getOwnerId()));
		Assert.notNull(shop, "提现申请查询失败，无此申请商户信息！");

		String settleRequestNo = IdUtil.simpleUUID();

		SettleSelfSettleApplyDto settleSelfSettleApplyDto = SettleSelfSettleApplyDto.builder()
			.parentMerchantNo(yeePayConst.getPlatformServiceNo())
			.merchantNo(shop.getMerchantNo())
			.settleRequestNo(settleRequestNo)
			.notifyUrl(csUrl(PaymentType.YEE_PAY_WITHDRAW.name()))
			.operatePeriod(YeepayApiConstant.operatePeriod.ALL)
			.build();

		bladeLogger.info("用户余额提现请求参数", settleSelfSettleApplyDto.toString());
		YopResponse yopResponse = yeepaySaasService.settleSelfSettleApply(settleSelfSettleApplyDto);
		JSONObject res = JSON.parseObject(yopResponse.getStringResult());

		withdrawRec.setRequestNo(settleRequestNo);

		if (!Objects.equals(res.getString("code"), "000000")) {
			withdrawRec.setWithdrawStatus(WithdrawStatus.FAIL.getValue());
			withdrawRec.setFailReason(res.getString("message"));
			withdrawRec.setResponseJson(res.toJSONString());
			withdrawRecService.updateById(withdrawRec);

			bladeLogger.info("userWithdraw-结算", res.toJSONString());
			throw new ServiceException(res.getString("message"));
		}

		withdrawRecService.updateById(withdrawRec);

		//支付参数
		SuccessParams successParams = SuccessParams.builder()
			.orderType(OrderType.USER_WITHDRAW.name())
			.status(WithdrawStatus.WAITING.getValue())
			.userId(withdrawRec.getOwnerId())
			.totalPrice(withdrawRec.getActualPrice())
			.shopId(withdrawRec.getOwnerId())
			.tenantId(withdrawRec.getTenantId())
			.withdrawRecId(withdrawRec.getId())
			.build();
		PaymentCache.putSuccessParams(Convert.toStr(settleRequestNo), successParams);

		return R.data(withdrawRec.getId());
	}

	/**
	 * @param payParam:
	 * @Return R
	 * @Author July
	 * @Description 用户积分提现
	 * @Date 2021/10/19 16:09
	 */
	@Override
	public R userPointWithdraw(PayParam payParam) {
		TransferRec transferRec = transferRecService.getById(payParam.getOrderId());
		Assert.notNull(transferRec, "订单查询失败，无此订单信息！");

		//从平台转账到用户的易宝商户账号中
		Shop shop = shopService.getById(transferRec.getOwnerId());
		String requestNo = IdUtil.simpleUUID();
		TransferOrderDto transferOrderDto = TransferOrderDto.builder()
			.fromMerchantNo(yeePayConst.getPlatformServiceNo())
			.toMerchantNo(shop.getMerchantNo())
			.orderAmount(transferRec.getPrice().toPlainString())
			.requestNo(requestNo)
			.usage("用户积分提现转账!")
			.feeChargeSide(transferRec.getFeeChargeSide())
			.notifyUrl(callbackUrl(PaymentType.YEE_PAY_WITHDRAW.name()))
			.build();
		transferOrderDto.setParentMerchantNo(yeePayConst.getPlatformServiceNo());

		bladeLogger.info("用户积分余额提现--转账请求参数", transferOrderDto.toString());
		YopResponse yopResponse = yeepaySaasService.transferOrder(transferOrderDto);
		JSONObject res = JSON.parseObject(yopResponse.getStringResult());
		if (!Objects.equals(res.getString("returnCode"), "UA00000")) {
			userPointWithdrawFail(transferRec, res);
			throw new ServiceException(res.getString("returnMsg"));
		}
		String transferStatus = res.getString("transferStatus");
		switch (transferStatus) {
			case "SUCCESS":
				userPointWithdrawSuccess(res, transferRec, requestNo);
				break;
			case "REQUEST_RECEIVE":
				PaymentCache.addTransferApply(HandleTransferData.builder()
					.transferRec(transferRec)
					.requestNo(requestNo)
					.build());
				break;
			default:
				userPointWithdrawFail(transferRec, res);
				break;
		}
		return R.data(transferRec.getWithdrawId());
	}

	/**
	 * @param transferRec: 转账信息
	 * @param res:         易宝返回数据
	 * @Return void
	 * @Author July
	 * @Description 处理转账申请失败
	 * @Date 2021/10/21 17:42
	 */
	private void userPointWithdrawFail(TransferRec transferRec, JSONObject res) {
		//更新提现记录
		WithdrawRec withdrawRec = withdrawRecService.getById(transferRec.getWithdrawId());
		withdrawRec.setWithdrawStatus(WithdrawStatus.FAIL.getValue());
		withdrawRec.setResponseJson(res.toJSONString());
		withdrawRec.setFailReason("转账环节出错：" + res.toJSONString());
		withdrawRecService.saveOrUpdate(withdrawRec);

		//更新转账记录
		transferRec.setTransferStatus(TransferStatus.FAIL);
		withdrawRec.setFailReason(res.toJSONString());
		transferRecService.saveOrUpdate(transferRec);

		bladeLogger.info("userPointWithdraw-转账", res.toJSONString());
	}

	/**
	 * @param requestNo:   商户请求号
	 * @param transferRec: 转账信息
	 * @param res:         易宝返回数据
	 * @Author July
	 * @Description 处理转账成功发起提现申请
	 * @Date 2021/10/21 17:42
	 */
	private void userPointWithdrawSuccess(JSONObject res, TransferRec transferRec, String requestNo) {

		//更新转账记录
		transferRec.setFee(res.getBigDecimal("fee"));
		transferRec.setTransferStatus(TransferStatus.SUCCESS);
		transferRec.setReceiveAmount(res.getBigDecimal("receiveAmount"));
		transferRec.setRequestNo(requestNo);
		transferRec.setResponseJson(res.toJSONString());
		transferRec.setThirdOrderId(res.getString("orderNo"));
		transferRecService.saveOrUpdate(transferRec);

		//支付参数
		SuccessParams successParams = SuccessParams.builder()
			.orderType(OrderType.USER_POINT_TRANSFER_WITHDRAW.name())
			.status(WithdrawStatus.WAITING.getValue())
			.userId(transferRec.getOwnerId())
			.totalPrice(transferRec.getReceiveAmount())
			.shopId(transferRec.getOwnerId())
			.tenantId(transferRec.getTenantId())
			.withdrawRecId(transferRec.getWithdrawId())
			.build();
		PaymentCache.putSuccessParams(Convert.toStr(requestNo), successParams);

		eventPublisher.publishEvent(new UserPointTransferWithdrawEvent(successParams));

	}

	/**
	 * @Author July
	 * @Description 处理用户积分提现的转账结果
	 * @Date 2021/10/22 10:29
	 */
	@Scheduled(cron = "0 */1 * * * ?")
	public void handleUserPointWithdrawTransferResult() {
		Set<HandleTransferData> queue = PaymentCache.getTransferApplyList();
		if (ObjectUtil.isNotEmpty(queue) && queue.size() > 0) {
			log.info("定时任务开始，查询队列中的待处理的转账记录！,队列数：{}", queue.size());
			queue.forEach(handleTransferData -> {
				//查询分账结果
				log.info("进行转账结果查询：{}", handleTransferData.getTransferRec().getId());
				TransferRec transferRec = transferRecService.getById(handleTransferData.getTransferRec().getId());
				Assert.notNull(transferRec, "转账订单查询失败，无此订单信息！" + transferRec.getId());

				TransferB2bQueryDto transferB2bQueryDto = TransferB2bQueryDto.builder()
					.parentMerchantNo(yeePayConst.getPlatformServiceNo())
					.requestNo(handleTransferData.getRequestNo())
					.build();
				YopResponse yopResponse = yeepaySaasService.transferB2bQuery(transferB2bQueryDto);
				JSONObject res = JSON.parseObject(yopResponse.getStringResult());
				if (!Objects.equals(res.getString("returnCode"), "UA00000")) {
					userPointWithdrawFail(transferRec, res);
					throw new ServiceException(res.getString("returnMsg"));
				}

				String transferStatus = res.getString("transferStatus");
				switch (transferStatus) {
					case "SUCCESS":
						userPointWithdrawSuccess(res, transferRec, handleTransferData.getRequestNo());
						PaymentCache.completeTransferApply(handleTransferData);
						break;
					case "FAIL":
						userPointWithdrawFail(transferRec, res);
						break;
					default:
						break;
				}
			});
		}
	}
}
