package com.bonus.canteen.core.account.v3.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.canteen.core.account.v3.constants.AccTradeTypeEnum;
import com.bonus.canteen.core.account.v3.metadata.enums.AccWalletIdEnum;
import com.bonus.canteen.core.account.v3.metadata.model.AccMetadataModel;
import com.bonus.canteen.core.account.v3.metadata.service.AccMetadataService;
import com.bonus.canteen.core.account.v3.po.AccPayPO;
import com.bonus.canteen.core.account.v3.po.AccWalletPayPO;
import com.bonus.canteen.core.account.v3.service.AccInfoHandleBusiness;
import com.bonus.canteen.core.account.v3.web.vo.AccInfoVO;
import com.bonus.canteen.core.account.v3.web.vo.AccWalletInfoVO;
import com.bonus.canteen.core.marketing.v2.rule.price.dto.RulePriceResultDTO;
import com.bonus.canteen.core.marketing.v2.rule.price.dto.RulePriceWalletDTO;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AccInfoHandleBusinessImpl implements AccInfoHandleBusiness {
   private static final Logger log = LoggerFactory.getLogger(AccInfoHandleBusinessImpl.class);
   @Autowired
   @Lazy
   private AccMetadataService accMetadataService;

   public AccPayPO amountCalculation(AccInfoVO accInfoVO, BigDecimal calculateAmount, List<Integer> walletIdList) {
      log.info("账户支付计算开始:accInfoVO={},calculateAmount={},walletIdList={}", new Object[]{JSONUtil.toJsonStr(accInfoVO), calculateAmount, JSONUtil.toJsonStr(walletIdList)});
      AccPayPO accPayPO = new AccPayPO();
      List<AccWalletPayPO> walletPayPOList = new ArrayList();
      Map<Integer, BigDecimal> merchantLimitConfig = this.getMerchantLimitConfig();
      log.info("账户支付计算,获取钱包最低限额:{}", JSONUtil.toJsonStr(merchantLimitConfig));
      Map<Integer, AccWalletInfoVO> walletInfoMap = (Map)accInfoVO.getWalletInfoList().stream().collect(Collectors.toMap(AccWalletInfoVO::getWalletId, Function.identity()));
      Iterator var8 = walletIdList.iterator();

      while(var8.hasNext()) {
         Integer walletId = (Integer)var8.next();
         if (calculateAmount.compareTo(BigDecimal.ZERO) <= 0) {
            break;
         }

         AccWalletInfoVO accWalletInfoVO = (AccWalletInfoVO)walletInfoMap.get(walletId);
         if (ObjectUtil.isNull(accWalletInfoVO)) {
            throw new ServiceException("acc_wallet_error_exception");
         }

         BigDecimal limitBalance = this.getLimitBalance(walletId, accWalletInfoVO.getLimitBalance(), merchantLimitConfig);
         BigDecimal bal = accWalletInfoVO.getWalletBal();
         BigDecimal canUseBal = NumberUtil.sub(bal, limitBalance);
         if (canUseBal.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal amount;
            if (canUseBal.compareTo(calculateAmount) >= 0) {
               bal = NumberUtil.sub(bal, calculateAmount);
               amount = (BigDecimal)ObjectUtil.clone(calculateAmount);
               calculateAmount = BigDecimal.ZERO;
            } else {
               amount = (BigDecimal)ObjectUtil.clone(canUseBal);
               bal = NumberUtil.sub(bal, amount);
               calculateAmount = NumberUtil.sub(calculateAmount, amount);
            }

            AccWalletPayPO walletPayPO = AccWalletPayPO.builder().amount(amount).custId(accInfoVO.getCustId()).walletBal(bal).walletId(walletId).build();
            walletPayPOList.add(walletPayPO);
         }
      }

      accPayPO.setWalletPayList(walletPayPOList);
      if (calculateAmount.compareTo(BigDecimal.ZERO) > 0) {
         accPayPO.setUnpaidBalance(calculateAmount);
      }

      return accPayPO;
   }

   protected BigDecimal getLimitBalance(Integer walletId, BigDecimal limitBalance, Map<Integer, BigDecimal> merchantLimitConfig) {
      return ObjectUtil.isNotNull(limitBalance) ? limitBalance : (BigDecimal)merchantLimitConfig.get(walletId);
   }

   public Map<Integer, BigDecimal> getMerchantLimitConfig() {
      Map<Integer, BigDecimal> limitConfig = Maps.newHashMap();
      limitConfig.put(AccWalletIdEnum.WALLET.getKey(), BigDecimal.ZERO);
      limitConfig.put(AccWalletIdEnum.SUBSIDY.getKey(), BigDecimal.ZERO);
      limitConfig.put(AccWalletIdEnum.LUCK_MONEY.getKey(), BigDecimal.ZERO);
      AccMetadataModel accMetadataModel = this.accMetadataService.queryMetadata();
      if (ObjectUtil.isNull(accMetadataModel)) {
         return limitConfig;
      } else {
         BigDecimal minWallet = ObjectUtil.isNotEmpty(accMetadataModel.getMinBalanceLimit()) ? new BigDecimal(accMetadataModel.getMinBalanceLimit()) : BigDecimal.ZERO;
         BigDecimal minSub = ObjectUtil.isNotEmpty(accMetadataModel.getMinSubBalanceLimit()) ? new BigDecimal(accMetadataModel.getMinSubBalanceLimit()) : BigDecimal.ZERO;
         BigDecimal minRed = ObjectUtil.isNotEmpty(accMetadataModel.getMinRedBalanceLimit()) ? new BigDecimal(accMetadataModel.getMinRedBalanceLimit()) : BigDecimal.ZERO;
         limitConfig.put(AccWalletIdEnum.WALLET.getKey(), minWallet);
         limitConfig.put(AccWalletIdEnum.SUBSIDY.getKey(), minSub);
         limitConfig.put(AccWalletIdEnum.LUCK_MONEY.getKey(), minRed);
         return limitConfig;
      }
   }

   @Override
   public AccPayPO amountCalculationByRulePrice(AccInfoVO accInfoVO, List<Integer> walletIdList, RulePriceResultDTO resultDTO) {
      if (!CollUtil.isEmpty(resultDTO.getWalletDetails()) && BigDecimal.ZERO.compareTo(resultDTO.getRealAmount()) < 0) {
         List<RulePriceWalletDTO> consumeList = (List)resultDTO.getWalletDetails().stream().filter((item) -> {
            return AccTradeTypeEnum.CONSUME.getKey().equals(item.getTradeType()) && AccWalletIdEnum.getAllWalletIdList()
                    .contains(item.getWalletId());
         }).collect(Collectors.toList());
         if (CollUtil.isEmpty(consumeList)) {
            return this.amountCalculation(accInfoVO, resultDTO.getRealAmount(), walletIdList);
         } else {
            Map<Integer, BigDecimal> merchantLimitConfig = this.getMerchantLimitConfig();
            Map<Integer, AccWalletInfoVO> walletInfoMap = (Map)accInfoVO.getWalletInfoList()
                    .stream().collect(Collectors.toMap(AccWalletInfoVO::getWalletId, Function.identity()));
            List<AccWalletPayPO> walletPayPOList = new ArrayList();
            Iterator var8 = consumeList.iterator();

            while(var8.hasNext()) {
               RulePriceWalletDTO rulePriceWalletDTO = (RulePriceWalletDTO)var8.next();
               walletPayPOList.add(this.getMktTimeQuotaAccWalletPay(walletInfoMap, merchantLimitConfig, rulePriceWalletDTO.getWalletId(), rulePriceWalletDTO.getAmount().intValue()));
            }

            AccPayPO accPayPO = new AccPayPO();
            accPayPO.setWalletPayList(walletPayPOList);
            accPayPO.setUnpaidBalance((BigDecimal)walletPayPOList.stream().map(AccWalletPayPO::getUnpaidBalance).filter(ObjectUtil::isNotNull).reduce(BigDecimal.ZERO, BigDecimal::add));
            return accPayPO;
         }
      } else {
         return this.amountCalculation(accInfoVO, resultDTO.getRealAmount(), walletIdList);
      }
   }

   protected AccWalletPayPO getMktTimeQuotaAccWalletPay(Map<Integer, AccWalletInfoVO> walletInfoMap, Map<Integer, BigDecimal> merchantLimitConfig, Integer walletId, Integer realAmount) {
      AccWalletInfoVO walletInfo = (AccWalletInfoVO)walletInfoMap.get(walletId);
      if (ObjectUtil.isNull(walletInfo)) {
         throw new ServiceException("acc_pay_mkttime_errot_exception");
      } else {
         BigDecimal limitBalance = (BigDecimal)merchantLimitConfig.get(walletId);
         BigDecimal bal = walletInfo.getWalletBal();
         BigDecimal canUseBal = NumberUtil.sub(bal, limitBalance);
         BigDecimal payAmount = new BigDecimal(realAmount);
         BigDecimal realAmountBd = BigDecimal.ZERO;
         if (canUseBal.compareTo(BigDecimal.ZERO) > 0) {
            if (canUseBal.compareTo(payAmount) > 0) {
               realAmountBd = payAmount;
            } else {
               realAmountBd = canUseBal;
            }
         }

         BigDecimal unpaidBalance = payAmount.subtract(realAmountBd);
         AccWalletPayPO accWalletPayPO = new AccWalletPayPO();
         accWalletPayPO.setWalletId(walletId);
         accWalletPayPO.setWalletBal(NumberUtil.sub(bal, realAmountBd));
         accWalletPayPO.setAmount(realAmountBd);
         accWalletPayPO.setCustId(walletInfo.getCustId());
         accWalletPayPO.setUnpaidBalance(unpaidBalance);
         return accWalletPayPO;
      }
   }
}
