
package com.tmerclub.cloud.multishop.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.bo.PayNotifyBO;
import com.tmerclub.cloud.common.order.vo.OrderSimpleAmountInfoBO;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.constant.DistributedIdKey;
import com.tmerclub.cloud.multishop.constant.ShopWalletAmountType;
import com.tmerclub.cloud.multishop.constant.ShopWalletChangeReason;
import com.tmerclub.cloud.multishop.dto.ShopWalletLogSearchDTO;
import com.tmerclub.cloud.multishop.mapper.ShopWalletMapper;
import com.tmerclub.cloud.multishop.mongo.MongoShopWalletLogBO;
import com.tmerclub.cloud.multishop.service.ShopWalletLogService;
import com.tmerclub.cloud.multishop.service.ShopWalletService;
import com.tmerclub.cloud.multishop.service.WalletLogOperationService;
import com.tmerclub.cloud.supplier.service.SupplierWalletService;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * 商家钱包信息
 *
 * @author tmerclub
 * @date 2021-01-07 16:41:03
 */
@Service
public class WalletLogOperationServiceImpl implements WalletLogOperationService {

    @Autowired
    private ShopWalletMapper shopWalletMapper;
    @Autowired
    private ShopWalletLogService shopWalletLogService;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RocketMQTemplate shopWalletNotifyServiceTemplate;
    @Autowired
    private SupplierWalletService supplierWalletService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ShopWalletService shopWalletService;
    private static final Logger logger = LoggerFactory.getLogger(WalletLogOperationServiceImpl.class);

    /**
     * TODO -supplier
     * @param orderSimpleAmountInfos 订单id和店铺id关联信息
     * @param message 订单id和店铺id关联信息
     * @param settledAmountMap 店铺已结算金额map
     * @param salesMap 商家的代销配置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountByOrder(List<OrderSimpleAmountInfoBO> orderSimpleAmountInfos, PayNotifyBO message, Map<Long, Long> settledAmountMap, Map<Long, Integer> salesMap) {

        // 进行分账，添加一个分账记录表，因为每一个订单进行分完帐后要进行退款，那么退款的时候也应该按照下单的时候的分账比例进行原路返回
        // 获取当前分账的比例
        List<MongoShopWalletLogBO> shopWalletLogList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        List<OrderSimpleAmountInfoBO> orderSupplierAmountInfos = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (OrderSimpleAmountInfoBO orderSimpleAmountInfo : orderSimpleAmountInfos) {
            Long settlementAmount;
            // 商家之所以会从一笔订单拿到钱，是因为订单支付成功
            // 幂等
            // 1. 商家应收 = 商品价格 - 商家优惠 - 分销金额
            // 2. 商家应收 = 用户支付 + 平台补贴 - 分销金额
            Long changeAmount;
            long supplierAmount;
            changeAmount = orderSimpleAmountInfo.getActualTotal() + orderSimpleAmountInfo.getPlatformAmount() - orderSimpleAmountInfo.getDistributionAmount() - orderSimpleAmountInfo.getPlatformCommission();
            if (!Objects.equals(orderSimpleAmountInfo.getSupplierId(), 0L)) {
                // 代销订单处理
                // 商家余额
                settlementAmount = settledAmountMap.get(orderSimpleAmountInfo.getShopId());
                // 如果商家收入金额小于供货价，0.直接转换为待采购订单（默认选中），1.通过商家已结算金额扣除差价
                Integer type = salesMap.get(orderSimpleAmountInfo.getShopId());
                // 供应商收入金额为 = 采购金额 + 运费 + 商家优惠运费 - 平台佣金
                supplierAmount = orderSimpleAmountInfo.getPurchaseAmount() + orderSimpleAmountInfo.getFreightAmount() + Math.abs(orderSimpleAmountInfo.getFreeFreightAmount());
                // 商家实际待结算金额 = 之前算法 - 运费 - 采购金额
                changeAmount = changeAmount - supplierAmount;
                // 如果商家收入金额小于供货价，判断处理
                // 代销设置为商家通过已结算账户金额扣除差价，且已结算金额小于于差价则变成待采购订单
                // 情况一，代销设置为商家直接转换成采购订单，且已结算金额小于差价则转换成采购订单
                // 情况二，代销设置为商家通过已结算账户金额扣除差价，且已结算金额小于于差价则变成待采购订单
                boolean checkAmountFlag = (Objects.equals(type, 0) && changeAmount < 0) || (Objects.equals(type, 1) && settlementAmount + changeAmount < 0);
                // 商家不同意采购，或者情况一，或者情况二，订单变成待采购
                if (!Objects.equals(message.getAgreePurchase(), 1) && checkAmountFlag) {
                    continue;
                }
                // 自动采购完成，代销设置为商家通过已结算账户金额自动扣除差价，且已结算金额足够，且改变金额小于0
                supplierAmount = supplierAmount - orderSimpleAmountInfo.getPurchasePlatformCommission();
                // 处理下供应商的分账
                handleSupplierAmount(orderSupplierAmountInfos, orderSimpleAmountInfo, changeAmount, supplierAmount, shopWalletLogList);
                // 待结算金额处理
                changeAmount = Math.max(0L, changeAmount);
            }

            // 商家添加未结算金额
            shopWalletMapper.addUnsettledAmount(orderSimpleAmountInfo.getShopId(), changeAmount, orderSimpleAmountInfo.getPaySysType());
            // 平台添加未结算金额（平台佣金 - 平台优惠分摊优惠金额）
            long platformChangeAmount = orderSimpleAmountInfo.getPlatformCommission() + orderSimpleAmountInfo.getPurchasePlatformCommission() - orderSimpleAmountInfo.getPlatformAmount();

            // 获取商家钱包记录列表
            saveShopWalletLog(orderSimpleAmountInfo, changeAmount, platformChangeAmount, shopWalletLogList);
        }
        if (CollectionUtil.isNotEmpty(orderSupplierAmountInfos)) {
            for (OrderSimpleAmountInfoBO orderSimpleAmountInfo : orderSupplierAmountInfos) {
                Long walletLogId = segmentManager.getSegmentId(DistributedIdKey.tmerclub_SHOP_WALLET_LOG);
                orderSimpleAmountInfo.setWalletLogId(walletLogId);
            }
            message.setOrderSimpleAmountInfos(orderSupplierAmountInfos);
        }

        // 分账总消息，包括分销分支&&订单采购&&供应商分账
        // 分销订单有自己的操作，分销订单不包含积分订单
        // 订单成功 ----> 商家分账成功 ----> 分销分账 ---> 供应商分账
        // 代销商品流程 订单成功 ----> 商家分账成功 ----> 分销分账 ---> 供应商分账 --->修改订单为待采购订单
        SendStatus shopWalletNotifyStatus = shopWalletNotifyServiceTemplate.syncSend(RocketMqConstant.SHOP_WALLET_NOTIFY_SERVICE_TOPIC, new GenericMessage<>(message)).getSendStatus();
        if (!Objects.equals(shopWalletNotifyStatus, SendStatus.SEND_OK)) {
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAmountBySettledOrder(Long orderId) {
        ShopWalletLogSearchDTO shopWalletLogSearchDTO = new ShopWalletLogSearchDTO();
        shopWalletLogSearchDTO.setOrderId(orderId);
        List<MongoShopWalletLogBO> shopWalletLogs = shopWalletLogService.listByParam(shopWalletLogSearchDTO);

        // 商家&平台需要结算的钱
        MongoShopWalletLogBO payLog = null;

        MongoShopWalletLogBO shopRefundLog = new MongoShopWalletLogBO();
        shopRefundLog.setUserAmount(0L);
        shopRefundLog.setDistributionAmount(0L);
        shopRefundLog.setPlatformAmount(0L);
        shopRefundLog.setShopPlatformCommission(0L);
        shopRefundLog.setPlatformCommission(0L);
        shopRefundLog.setShopChangeAmount(0L);
        shopRefundLog.setPlatformChangeAmount(0L);
        for (MongoShopWalletLogBO shopWalletLog : shopWalletLogs) {
            // 下单
            MongoShopWalletLogBO shopPayLog = polymerizationShopWalletLog(shopRefundLog, shopWalletLog);
            if (shopPayLog != null) {
                payLog = shopPayLog;
            }
        }
        // 非积分订单需要保存店铺结算记录
        if (Objects.nonNull(payLog)) {
            // 通知供应商供应商结算
            supplierWalletService.addAmountBySettledOrder(orderId);
        }

    }


    private void saveShopWalletLog(OrderSimpleAmountInfoBO orderSimpleAmountInfo, Long changeAmount, long platformChangeAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
        shopWalletLog.setOrderId(orderSimpleAmountInfo.getOrderId());
        shopWalletLog.setCreateTime(new Date());
        shopWalletLog.setUpdateTime(new Date());
        shopWalletLog.setReason(ShopWalletChangeReason.PAY.value());
        shopWalletLog.setShopId(orderSimpleAmountInfo.getShopId());
        shopWalletLog.setAmountType(ShopWalletAmountType.UNSETTLED_AMOUNT.value());
        shopWalletLog.setPlatformIoType(platformChangeAmount >= 0 ? 1 : 0);

        shopWalletLog.setUserAmount(orderSimpleAmountInfo.getActualTotal());
        shopWalletLog.setPlatformCommission(orderSimpleAmountInfo.getPlatformCommission() + orderSimpleAmountInfo.getPurchasePlatformCommission());
        shopWalletLog.setPlatformChangeAmount(shopWalletLog.getUserAmount());
        shopWalletLog.setDistributionAmount(orderSimpleAmountInfo.getDistributionAmount());
        shopWalletLog.setPlatformAmount(orderSimpleAmountInfo.getPlatformAmount());

        shopWalletLog.setShopReduceAmount(orderSimpleAmountInfo.getShopAmount());
        shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());

        if (!Objects.equals(orderSimpleAmountInfo.getShopId(), Constant.PLATFORM_SHOP_ID)) {
            shopWalletLog.setShopIoType(1);
            shopWalletLog.setShopChangeAmount(changeAmount);
            shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());
            shopWalletLog.setPlatformChangeAmount(Math.abs(platformChangeAmount));
        }
        shopWalletLogList.add(shopWalletLog);
    }

    /**
     * 供应商采购金额处理，如果商家待结算金额改变金额小于0，减少结算金额并添加日志
     * @param orderSupplierAmountInfos 采购分账信息
     * @param orderSimpleAmountInfo 订单信息
     * @param changeAmount 商家待结算改变金额
     * @param supplierAmount 供应商收入金额
     */
    private void handleSupplierAmount(List<OrderSimpleAmountInfoBO> orderSupplierAmountInfos, OrderSimpleAmountInfoBO orderSimpleAmountInfo,
                                      Long changeAmount, Long supplierAmount, List<MongoShopWalletLogBO> shopWalletLogList) {
        OrderSimpleAmountInfoBO supplierAmountBO = new OrderSimpleAmountInfoBO();
        supplierAmountBO.setOrderId(orderSimpleAmountInfo.getOrderId());
        supplierAmountBO.setPurchasePlatformCommission(orderSimpleAmountInfo.getPurchasePlatformCommission());
        supplierAmountBO.setPurchaseAmount(supplierAmount);
        supplierAmountBO.setActualTotal(orderSimpleAmountInfo.getActualTotal());
        supplierAmountBO.setSupplierId(orderSimpleAmountInfo.getSupplierId());
        supplierAmountBO.setFreightAmount(orderSimpleAmountInfo.getFreightAmount());
        supplierAmountBO.setFreeFreightAmount(orderSimpleAmountInfo.getFreeFreightAmount());
        orderSupplierAmountInfos.add(supplierAmountBO);

        if (changeAmount < 0) {
            // 代销订单，商家减少结算金额并添加日志
            boolean isSuccess = shopWalletService.subSettledAmount(orderSimpleAmountInfo.getShopId(), Math.abs(changeAmount), orderSimpleAmountInfo.getPaySysType());
            if (!isSuccess) {
                throw new LuckException("店铺结算金额不足");
            }
            // 添加日志
            MongoShopWalletLogBO shopWalletLog = new MongoShopWalletLogBO();
            shopWalletLog.setCreateTime(new Date());
            shopWalletLog.setUpdateTime(shopWalletLog.getCreateTime());
            shopWalletLog.setOrderId(orderSimpleAmountInfo.getOrderId());
            shopWalletLog.setReason(ShopWalletChangeReason.PURCHASE_SPREAD_AMOUNT.value());
            shopWalletLog.setShopId(orderSimpleAmountInfo.getShopId());
            shopWalletLog.setShopIoType(0);
            shopWalletLog.setShopChangeAmount(changeAmount);
            shopWalletLog.setAmountType(ShopWalletAmountType.SETTLED_AMOUNT.value());
            shopWalletLog.setUserAmount(orderSimpleAmountInfo.getActualTotal());
            shopWalletLog.setDistributionAmount(orderSimpleAmountInfo.getDistributionAmount());
            shopWalletLog.setShopReduceAmount(orderSimpleAmountInfo.getShopAmount());
            shopWalletLog.setPlatformAmount(orderSimpleAmountInfo.getPlatformAmount());
            shopWalletLog.setShopPlatformCommission(orderSimpleAmountInfo.getPlatformCommission());
            // 添加日志
            shopWalletLogList.add(shopWalletLog);
        }
    }

    /**
     * 聚合订单支付退款导致商家金额变更的记录
     * @param refundWalletLog
     * @param shopWalletLog
     * @return
     */
    private MongoShopWalletLogBO polymerizationShopWalletLog(MongoShopWalletLogBO refundWalletLog, MongoShopWalletLogBO shopWalletLog) {
        if (Objects.equals(ShopWalletChangeReason.PAY.value(), shopWalletLog.getReason())) {
            return shopWalletLog;
        }
        // 退款
        else if (Objects.equals(ShopWalletChangeReason.ORDER_REFUND.value(), shopWalletLog.getReason())) {
            refundWalletLog.setUserAmount(refundWalletLog.getUserAmount() + shopWalletLog.getUserAmount());
            refundWalletLog.setDistributionAmount(refundWalletLog.getDistributionAmount() + shopWalletLog.getDistributionAmount());
            refundWalletLog.setPlatformAmount(refundWalletLog.getPlatformAmount() + shopWalletLog.getPlatformAmount());
            refundWalletLog.setShopPlatformCommission(refundWalletLog.getShopPlatformCommission() +
                    (Objects.nonNull(shopWalletLog.getShopPlatformCommission()) ? shopWalletLog.getShopPlatformCommission() : 0L));
            refundWalletLog.setPlatformCommission(refundWalletLog.getPlatformCommission() +
                    (Objects.nonNull(shopWalletLog.getPlatformCommission()) ? shopWalletLog.getPlatformCommission() : 0L));
            // 如果因为退款收入，实际上是减少了退款的钱
            Long shopChangeAmount = (Objects.isNull(shopWalletLog.getShopIoType()) ? 0L :
                    (shopWalletLog.getShopIoType() == 1) ? -shopWalletLog.getShopChangeAmount() : shopWalletLog.getShopChangeAmount());
            Long platformChangeAmount = (Objects.isNull(shopWalletLog.getPlatformIoType()) ? 0L :
                    (shopWalletLog.getPlatformIoType() == 1) ? -shopWalletLog.getPlatformChangeAmount() : shopWalletLog.getPlatformChangeAmount());
            refundWalletLog.setShopChangeAmount(refundWalletLog.getShopChangeAmount() + shopChangeAmount);
            refundWalletLog.setPlatformChangeAmount(refundWalletLog.getPlatformChangeAmount() + platformChangeAmount);
        }
        return null;
    }
}
