package org.springblade.payment.callback.trade;


import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.enums.AppConstant;
import org.springblade.common.enums.CsFlag;
import org.springblade.common.enums.OrderType;
import org.springblade.common.enums.ResCode;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.tenant.mp.TenantEntity;
import org.springblade.core.tool.utils.AesUtil;
import org.springblade.gateway.common_gateway.entity.vo.BillSuccessEvent;
import org.springblade.ldt.bills.entity.BalanceBills;
import org.springblade.ldt.bills.entity.Bills;
import org.springblade.ldt.bills.entity.PointBills;
import org.springblade.ldt.bills.service.IBalanceBillsService;
import org.springblade.ldt.bills.service.IBillsService;
import org.springblade.ldt.bills.service.IPointBillsService;
import org.springblade.ldt.mall.entity.Mall;
import org.springblade.ldt.mall.service.IMallService;
import org.springblade.ldt.shop.entity.Shop;
import org.springblade.ldt.shop.service.IShopService;
import org.springblade.ldt.user.entity.LoginUser;
import org.springblade.ldt.user.entity.Member;
import org.springblade.ldt.user.entity.UserChannelPoint;
import org.springblade.ldt.user.service.ILoginUserService;
import org.springblade.ldt.user.service.IMemberService;
import org.springblade.ldt.user.service.IUserChannelPointService;
import org.springblade.payment.entity.PaymentVO;
import org.springblade.payment.entity.SuccessParams;
import org.springblade.payment.event.UserPayCsEvent;
import org.springblade.payment.handle.handler.DataHandle;
import org.springblade.payment.listener.SubAccountHandle;
import org.springblade.webSocket.WebSocketServer;
import org.springblade.wx.config.ConfigForShop;
import org.springblade.wx.constant.MessageTemplate;
import org.springblade.wx.constant.MiniProgramType;
import org.springblade.wx.service.WeChatService;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author: lianghanqiang
 * @description: 用户付款回调
 * @since: 8/5/21 -- 3:01 PM
 */
@Component
@AllArgsConstructor
@Slf4j
public class UserPayCS {

	private ILoginUserService loginUserService;
	private IBillsService billsService;
	private IPointBillsService pointBillsService;
	private IBalanceBillsService balanceBillsService;
	private IShopService shopService;
	private IUserChannelPointService userChannelPointService;
	private ApplicationEventPublisher applicationEventPublisher;
	private DataHandle dataHandle;
	private IMemberService memberService;
	private IMallService mallService;
	private WeChatService weChatService;
	private ConfigForShop configForShop;
	private WebSocketServer webSocketServer;
	private SubAccountHandle subAccountHandle;


	@SneakyThrows
	@EventListener
	//@Async
	public void PaySuccess(UserPayCsEvent userPayCsEvent) {
		SuccessParams successParams = userPayCsEvent.getSuccessParams();
		PaymentVO paymentVO = PaymentVO.builder().isSuccess(Boolean.FALSE).msg("订单付款异常！").build();
		try {
			//获取对应交易的积分账单、余额账单、支付账单以及交易用户

			synchronized (successParams.getBills().getPayId().toString().intern()) {
				LoginUser user = loginUserService.getById(successParams.getBills().getPayId());
				Bills bills = billsService.getById(successParams.getBills().getId());
				//回调已处理，忽略后续回调
				if (bills.getCsFlag().isCheck()) {
					return;
				}

				//socket 通知
				paymentVO.setIsSuccess(Boolean.TRUE);
				paymentVO.setMsg("交易成功！");
				paymentVO.setTotalPrice(successParams.getTotalPrice());

				//记录第三方支付平台订单号
				bills.setThirdOrderId(successParams.getThirdOrderId());

				PointBills pointBills = Objects.isNull(successParams.getPointBillsId()) ? null : pointBillsService.getById(successParams.getPointBillsId());
				BalanceBills balanceBills = Objects.isNull(successParams.getBalanceBillsId()) ? null : balanceBillsService.getById(successParams.getBalanceBillsId());
				//获取渠道积分收取的服务费
				BigDecimal pointBillFee = Optional.ofNullable(pointBills)
					.map(PointBills::getFee).orElse(BigDecimal.ZERO);
				//获取积分余额收取的服务费
				BigDecimal balanceBillFee = Optional.ofNullable(balanceBills)
					.map(BalanceBills::getFee).orElse(BigDecimal.ZERO);
				//获取支付平台收取的服务费
				BigDecimal merchantFee = BigDecimal.ZERO;
				BigDecimal ypSettleAmount = BigDecimal.ZERO;

				if (!Objects.isNull(successParams.getRes())) {
					ypSettleAmount = successParams.getRes().getBigDecimal("ypSettleAmount");
					merchantFee = successParams.getRes().getBigDecimal("merchantFee");
					bills.setMerchantFee(merchantFee);
					bills.setReceiveNumYp(ypSettleAmount);
					bills.setReceiveNumTotal(bills.getBalanceNum().add(bills.getPointNum().add(ypSettleAmount)));
					if (Objects.isNull(bills.getReceiveNumTotal())) {
						bills.setActualIncome(bills.getCost());
					} else {
						bills.setActualIncome(bills.getReceiveNumTotal());
					}
				}

				//账单的总积分服务费
				bills.setPointFee(balanceBillFee.add(pointBillFee));
				//账单的总收取的价格（商品价格 + 国信服务费 +积分服务费）
				BigDecimal totalPrice = bills.getCost().add(bills.getPointFee()).add(Optional.ofNullable(bills.getFee()).orElse(BigDecimal.ZERO));
				bills.setTotalPrice(totalPrice);

				//获取待更新的各类型信息
				List<TenantEntity> tenantEntities = handlePointBills(pointBills, user);
				UserChannelPoint channelPoint = (UserChannelPoint) tenantEntities.get(1);
				pointBills = (PointBills) tenantEntities.get(0);
				balanceBills = handleBalanceBills(balanceBills, user);
//				bills.setPayStatus(AppConstant.BillPayStatus.已完结.name());
				bills.setCsFlag(CsFlag.CHECK);
				Shop shop = updateShopBalance(bills, ypSettleAmount);

				List<TenantEntity> handleSendPointBills = handleSendPointBills(channelPoint, bills, user);
				PointBills sendPointBills = (PointBills) handleSendPointBills.get(0);
				UserChannelPoint sendUserChanelPoint = (UserChannelPoint) handleSendPointBills.get(1);
				Member mallMember = (Member) handleSendPointBills.get(2);
				//更新信息
				updateStatus(channelPoint, pointBills, bills, user, shop, balanceBills, sendPointBills, sendUserChanelPoint, mallMember);

				//通知处理代理费，申请分账
				successParams.setBills(bills);
				subAccountHandle.accountHandle(new BillSuccessEvent(successParams));
//				applicationEventPublisher.publishEvent(new BillSuccessEvent(successParams));

				String openId = shop.getOpenId();
				String time = DateUtil.format(bills.getUpdateTime(), "yyyy年MM月dd日 HH:mm:ss");
				String contentStr = String.format(MessageTemplate.PAYMENT_NOTICE.getContentString(), bills.getCost(), bills.getPayStatus(), bills.getId(), time);
				JSONObject content = JSON.parseObject(contentStr);
				weChatService.sendTemplateMessage(openId, MessageTemplate.PAYMENT_NOTICE, content, configForShop.getPaymentPage(), MiniProgramType.SHOP);
			}
		} catch (Exception e) {
			log.error("清算回调异常：{}", e.getMessage());
			e.printStackTrace();
			throw e;
		} finally {
			webSocketServer.sendInfo(
				AppConstant.SOCKET_PRE.USER.getName() + successParams.getUserId(),
				JSON.toJSONString(paymentVO)
			);
			webSocketServer.sendInfo(
				AppConstant.SOCKET_PRE.SHOP.getName() + successParams.getShopId(),
				JSON.toJSONString(paymentVO)
			);
		}
	}

	/**
	 * 更新余额状态
	 *
	 * @param channelPoint
	 * @param pointBills
	 * @param bills
	 * @param user
	 * @param shop
	 * @param balanceBills
	 * @param sendPointBills
	 * @param sendUserChanelPoint
	 * @param mallMember
	 */
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	void updateStatus(UserChannelPoint channelPoint,
					  PointBills pointBills,
					  Bills bills,
					  LoginUser user,
					  Shop shop,
					  BalanceBills balanceBills,
					  PointBills sendPointBills,
					  UserChannelPoint sendUserChanelPoint, Member mallMember) throws ServiceException {

		//更新渠道积分
		Assert.isTrue(dataHandle.saveOrUpdateEntity(userChannelPointService, channelPoint), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//更新渠道积分订单
		Assert.isTrue(dataHandle.saveOrUpdateEntity(pointBillsService, pointBills), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//更新普通积分订单
		Assert.isTrue(dataHandle.saveOrUpdateEntity(balanceBillsService, balanceBills), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//更新订单信息
		Assert.isTrue(dataHandle.saveOrUpdateEntity(billsService, bills), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//更新渠道积分订单信息
		Assert.isTrue(dataHandle.saveOrUpdateEntity(pointBillsService, sendPointBills), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//更新渠道积分账户信息
		Assert.isTrue(dataHandle.saveOrUpdateEntity(userChannelPointService, sendUserChanelPoint), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//修改商户账户信息
		Assert.isTrue(dataHandle.saveOrUpdateEntity(shopService, shop), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//修改用户积分余额
		Assert.isTrue(dataHandle.saveOrUpdateEntity(loginUserService, user), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});

		//修改商场会员信息
		Assert.isTrue(dataHandle.saveOrUpdateEntity(memberService, mallMember), () -> {
			throw new ServiceException(ResCode.USER_PAY_CALLBACK_ERROR);
		});
	}


	/**
	 * 修改用户积分以及积分账单状态
	 *
	 * @return
	 */
	private List<TenantEntity> handlePointBills(PointBills pointBills, LoginUser user) {
		UserChannelPoint channelPoint = null;
		if (!Objects.isNull(pointBills)) {
			channelPoint = userChannelPointService.getById(pointBills.getChannelId());
			channelPoint.setAvailable(channelPoint.getAvailable().subtract(pointBills.getPrice()).subtract(pointBills.getFee()));
			//user.setChannelPoint(user.getChannelPoint().subtract(pointBills.getPrice()).subtract(pointBills.getFee()));
			user.setFrozenPoint(user.getFrozenPoint().subtract(pointBills.getPrice().add(pointBills.getFee())));
			pointBills.setPayStatus(AppConstant.BillPayStatus.付款成功.name());
		}
		return Arrays.asList(pointBills, channelPoint);
	}

	/**
	 * 修改用户账户余额以及余额账单状态
	 *
	 * @return
	 */
	private BalanceBills handleBalanceBills(BalanceBills balanceBills, LoginUser user) {
		if (!Objects.isNull(balanceBills)) {
			//user.setBalance(user.getBalance().subtract(balanceBills.getPrice()).subtract(balanceBills.getFee()));
			user.setFrozenBalance(user.getFrozenBalance().subtract(balanceBills.getPrice().add(balanceBills.getFee())));
			balanceBills.setPayStatus(AppConstant.BillPayStatus.付款成功.name());
		}
		return balanceBills;
	}

	/**
	 * 商场赠送用户积分
	 *
	 * @return
	 */
	private List<TenantEntity> handleSendPointBills(UserChannelPoint userChannelPoint, Bills bills, LoginUser user) {
		PointBills pointBills = pointBillsService.getOne(new QueryWrapper<PointBills>().lambda().eq(PointBills::getTradeNo, bills.getId()).eq(PointBills::getType, OrderType.MALL_SEND.name()));
		Member member = null;
		if (!Objects.isNull(pointBills)) {
			user.setChannelPoint(user.getChannelPoint().add(pointBills.getPrice()));
			if (Objects.isNull(userChannelPoint)) {
				userChannelPoint = userChannelPointService.getOne(new QueryWrapper<UserChannelPoint>().lambda()
					.eq(UserChannelPoint::getUserId, user.getId())
					.eq(UserChannelPoint::getChannelId, pointBills.getChannelId()));
				if (Objects.isNull(userChannelPoint)) {
					userChannelPoint = initUserChannelPoint(user, pointBills);
				}
			}
			userChannelPoint.setTotalPoint(userChannelPoint.getTotalPoint().add(pointBills.getPrice()));
			userChannelPoint.setAvailable(userChannelPoint.getAvailable().add(pointBills.getPrice()));

			//店铺会员管理
			Mall mall = mallService.getById(pointBills.getPayId());
			member = memberService.getOne(new QueryWrapper<Member>().lambda()
				.eq(Member::getMallId, pointBills.getPayId())
				.eq(Member::getType, AppConstant.PLATFORM.MALL.name())
				.eq(Member::getUserId, user.getId()));
			if (Objects.isNull(member)) {
				member = initMember(mall, user, bills);
			}
			member.setPayTotal(member.getPayTotal().add(bills.getPrice()));
			member.setPointTotal(member.getPointTotal().add(pointBills.getSendNum()));
			member.setPointValue(member.getPointValue().add(pointBills.getPrice()));
			pointBills.setPayStatus(AppConstant.BillPayStatus.付款成功.name());
		}

		return Arrays.asList(pointBills, userChannelPoint, member);
	}

	/**
	 * 初始化商场渠道信息
	 *
	 * @param user
	 * @param pointBills
	 * @return
	 */
	private UserChannelPoint initUserChannelPoint(LoginUser user, PointBills pointBills) {
		UserChannelPoint userChannelPoint = new UserChannelPoint();
		userChannelPoint.setAvailable(BigDecimal.ZERO);
		userChannelPoint.setTotalPoint(BigDecimal.ZERO);
		userChannelPoint.setUsedPoint(BigDecimal.ZERO);
		userChannelPoint.setUserId(user.getId());
		//账单渠道信息
		userChannelPoint.setChannelName(pointBills.getChannelName());
		userChannelPoint.setChannelId(pointBills.getChannelId());
		userChannelPoint.setChannelLogo(pointBills.getChannelLogo());
		userChannelPoint.setPointRate(pointBills.getChannelPointRate());
		return userChannelPoint;
	}

	/**
	 * 商家账户余额更新
	 *
	 * @param bills
	 * @param ypSettleAmount
	 * @return
	 */
	public Shop updateShopBalance(Bills bills, BigDecimal ypSettleAmount) {
		Shop shop = null;
		if (!Objects.isNull(bills)) {
			shop = shopService.getById(bills.getReceiveId());
			shop.setBalance(shop.getBalance()
				.add(ypSettleAmount)
			);
			shop.setTotalBalance(shop.getTotalBalance().add(ypSettleAmount));
			shop.setCharge(shop.getCharge().add(bills.getPointNum()));
			shop.setPoint(shop.getPoint().add(Optional.ofNullable(bills.getPointNum()).orElse(BigDecimal.ZERO)));
			shop.setTotalPoint(shop.getTotalPoint().add(Optional.ofNullable(bills.getPointNum()).orElse(BigDecimal.ZERO)));
		}
		return shop;
	}

	/**
	 * 初始化商场会员
	 */
	private Member initMember(Mall mall, LoginUser user, Bills bills) {
		Member member = new Member();
		member.setMallId(mall.getId());
		member.setUserId(user.getId());
		member.setPointValue(BigDecimal.ZERO);
		member.setPointTotal(BigDecimal.ZERO);
		member.setPayTotal(BigDecimal.ZERO);
		member.setAvatar(user.getAvatar());
		member.setNickName(user.getNickName());
		member.setPhone(user.getPhone());
		member.setType(AppConstant.PLATFORM.MALL.name());
		member.setTenantId(bills.getTenantId());
		return member;
	}

}



