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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.core.account.v3.api.dto.AccOrdBaseDTO;
import com.bonus.core.account.v3.api.dto.AccOrdRefundDTO;
import com.bonus.core.account.v3.constants.AccTradeStateEnum;
import com.bonus.core.account.v3.constants.AccTradeTypeEnum;
import com.bonus.core.account.v3.constants.AccUpdateWalletOperateEnum;
import com.bonus.core.account.v3.custom.AccPayUniqueCustomBusiness;
import com.bonus.core.account.v3.metadata.enums.AccWalletIdEnum;
import com.bonus.core.account.v3.metadata.model.AccMetadataModel;
import com.bonus.core.account.v3.metadata.service.AccMetadataService;
import com.bonus.core.account.v3.model.AccPayFrozenDetail;
import com.bonus.core.account.v3.model.AccTradeWalletDetail;
import com.bonus.core.account.v3.mq.send.AccSendMq;
import com.bonus.core.account.v3.po.*;
import com.bonus.core.account.v3.service.*;
import com.bonus.core.account.v3.utils.AccUtils;
import com.bonus.core.account.v3.web.vo.*;
import com.bonus.core.marketing.v2.api.MarketApi;
import com.bonus.core.marketing.v2.rule.pay.dto.RulePayComputeDTO;
import com.bonus.core.marketing.v2.rule.pay.dto.RulePayResultDTO;
import com.bonus.core.marketing.v2.rule.price.dto.RulePriceResultDTO;
import com.bonus.core.pay.common.constants.PayChannelEnum;
import com.bonus.core.pay.common.constants.PayStateEnum;
import com.bonus.core.pay.common.constants.PayTypeEnum;
import com.bonus.i18n.I18n;
import com.bonus.utils.id.Id;
import com.google.common.collect.Lists;
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 org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class AccPayServiceImpl implements AccPayService {
   private static final Logger log = LoggerFactory.getLogger(AccPayServiceImpl.class);
   @Resource
   @Lazy
   private AccInfoService accInfoService;
   @Resource
   @Lazy
   private MarketApi marketApi;
   @Resource
   @Lazy
   private AccMetadataService accMetadataService;
   @Resource
   @Lazy
   private AccTradeService accTradeService;
   @Resource
   @Lazy
   private AccWalletInfoService accWalletInfoService;
   @Resource
   @Lazy
   private AccInfoHandleBusiness accInfoHandleBusiness;
   @Resource
   @Lazy
   private AccPayUniqueCustomBusiness accPayUniqueCustomBusiness;
   @Resource
   @Lazy
   private AccBusinessService accBusinessService;
   @Resource
   @Lazy
   private AccTradeWalletDetailService accTradeWalletDetailService;

   @Override
   @Transactional(
      rollbackFor = {Exception.class},
      propagation = Propagation.REQUIRES_NEW
   )
   public AccOrdBasicsVO accPayForOffline(AccOrdBaseDTO accOrdOfflineDTO) {
      log.info("[离线订单支付]入参_{}", JSON.toJSONString(accOrdOfflineDTO));
      AccUtils.checkAccPayUpgradeTask(accOrdOfflineDTO.getCustId());

      AccOrdBasicsVO var3;
      try {
         AccOrdBasicsVO accOrdBasicsVO = this.accPayUniqueCustomBusiness.prepAccPayForOffline(accOrdOfflineDTO);
         if (!ObjectUtil.isNotNull(accOrdBasicsVO)) {
            AccInfoVO accInfoVO = this.getAccInfoByCustId(accOrdOfflineDTO.getCustId());
            List<Integer> walletIdList = this.getWalletPayIdList(accOrdOfflineDTO);
            log.info("[离线订单支付]获取钱包扣款顺序:{}", JSONUtil.toJsonStr(walletIdList));
            AccPayPO accPayPO = this.accInfoHandleBusiness.amountCalculationByRulePrice(accInfoVO, walletIdList, accOrdOfflineDTO.getRulePriceResult());
            log.info("[离线订单支付]计算金额明细：{}", JSON.toJSONString(accPayPO));
            this.accPayUniqueCustomBusiness.didOfflinePayCalculation(accPayPO, accOrdOfflineDTO);
            List<AccWalletPayPO> walletPayPOList = accPayPO.getWalletPayList();
            if (ObjectUtil.isNotNull(accPayPO.getUnpaidBalance()) && accPayPO.getUnpaidBalance().compareTo(BigDecimal.ZERO) > 0) {
               this.addUnpaidBalanceAccPay(accInfoVO, walletPayPOList, accPayPO.getUnpaidBalance());
            }

            BigDecimal realAmount = (BigDecimal)walletPayPOList.stream().map(AccWalletPayPO::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            AccConsumeWalletVO accConsumeWalletVO = this.accBusinessService.doConsumeUpdateWalletAndAddTradeV2((new AccConsumeUpdateWalletPO()).setCustId(accInfoVO.getCustId()).setPsnType(accInfoVO.getPsnType()).setOrgId(accInfoVO.getOrgId()).setTradeType(AccTradeTypeEnum.CONSUME.getKey()).setAmount(realAmount).setMachineSn(accOrdOfflineDTO.getMchSn()).setRemark(accOrdOfflineDTO.getRemark()).setOrderTradeId(accOrdOfflineDTO.getOrderId()).setWalletPayList(walletPayPOList).setPayWalletIdList(walletIdList));
            AccOrdBasicsVO var9 = this.assembledAccOrdTradeDetailV2(accConsumeWalletVO, walletPayPOList);
            return var9;
         }

         var3 = accOrdBasicsVO;
      }  catch (Exception var15) {
         log.info("[离线订单支付]系统错误：{}", var15.getMessage(), var15);
         throw new ServiceException("[离线订单支付]系统错误");
      } finally {
         AccUtils.accPayUnLockByCustId(accOrdOfflineDTO.getCustId());
      }

      return var3;
   }


   @Override
   @Transactional(
      rollbackFor = {Exception.class},
      propagation = Propagation.REQUIRES_NEW
   )
   public AccOrdBasicsVO accOrdForRefund(AccOrdRefundDTO accOrdRefundDTO) {
      log.info("[退款订单]入参_{}", JSON.toJSONString(accOrdRefundDTO));
      AccUtils.checkAccPayUpgradeTask(accOrdRefundDTO.getCustId());

      AccOrdBasicsVO var12;
      try {
         if (accOrdRefundDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            AccOrdBasicsVO var20 = new AccOrdBasicsVO();
            return var20;
         }

         AccInfoVO accInfoVO = this.getAccInfoByCustId(accOrdRefundDTO.getCustId());
         List<AccTradeWalletDetail> payTradeList = this.accTradeWalletDetailService.queryAccTradeWalletDetailByLeOrdNo(accOrdRefundDTO.getOrderId(), Arrays.asList(AccTradeTypeEnum.CONSUME.getKey(), AccTradeTypeEnum.CONSUME_REPAIR.getKey()));
         log.info("[退款订单]根据订单id查询账户消费数据,orderId={}，size={}", accOrdRefundDTO.getOrderId(), payTradeList.size());
         if (ObjectUtil.isEmpty(payTradeList)) {
            throw new ServiceException("acc_ord_not_exits_record");
         }

         BigDecimal orderAmount = (BigDecimal)payTradeList.stream().map(AccTradeWalletDetail::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
         Long originTradeId = ((AccTradeWalletDetail)payTradeList.get(0)).getTradeId();
         List<AccTradeWalletDetail> refundTradeList = this.accTradeWalletDetailService.queryAccTradeWalletDetailByOriginTradeId(originTradeId, AccTradeTypeEnum.CONSUME_REFUND.getKey());
         BigDecimal refundedAmount = BigDecimal.ZERO;
         Map<Integer, BigDecimal> refundedMap = Maps.newHashMap();
         if (ObjectUtil.isNotEmpty(refundTradeList)) {
            refundedAmount = (BigDecimal)refundTradeList.stream().map(AccTradeWalletDetail::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            refundedMap = (Map)refundTradeList.stream().collect(Collectors.groupingBy(AccTradeWalletDetail::getWalletId, Collectors.reducing(BigDecimal.ZERO, AccTradeWalletDetail::getAmount, BigDecimal::add)));
            log.info("[退款订单]根据订单id查询已退款金额,orderId={}，refundedAmount={}", originTradeId, refundedAmount);
         }

         if (orderAmount.compareTo(refundedAmount.add(accOrdRefundDTO.getAmount())) < 0) {
            throw new ServiceException("acc_refund_amount_exception");
         }

         List<AccWalletPayPO> walletRefundPayList = Lists.newArrayList();
         long tradeId = this.accOrdForRefundHandle(accInfoVO, payTradeList, (Map)refundedMap, accOrdRefundDTO, originTradeId, walletRefundPayList);
         var12 = this.assembledAccOrdTradeDetail(accInfoVO.getCustId(), tradeId, walletRefundPayList);
      } catch (Exception var18) {
         log.info("[退款订单]系统错误：{}", var18.getMessage(), var18);
         throw new ServiceException("[退款订单]系统错误");
      } finally {
         AccUtils.accPayUnLockByCustId(accOrdRefundDTO.getCustId());
      }

      return var12;
   }

   protected Long accOrdForRefundHandle(AccInfoVO accInfoVO, List<AccTradeWalletDetail> payTradeList, Map<Integer, BigDecimal> refundedMap, AccOrdRefundDTO accOrdRefundDTO, Long originTradeId, List<AccWalletPayPO> walletRefundPayList) {
      long tradeId = Id.next();
      LocalDateTime now = LocalDateTime.now();
      Integer tradeType = AccTradeTypeEnum.CONSUME_REFUND.getKey();
      BigDecimal remainingAmount = accOrdRefundDTO.getAmount();
      Iterator var12 = payTradeList.iterator();

      while(var12.hasNext()) {
         AccTradeWalletDetail payTrade = (AccTradeWalletDetail)var12.next();
         Integer walletId = payTrade.getWalletId();
         BigDecimal walletRefundedAmount = ObjectUtil.isNotNull(refundedMap.get(walletId)) ? (BigDecimal)refundedMap.get(walletId) : BigDecimal.ZERO;
         BigDecimal walletCanUseAmount = payTrade.getAmount().subtract(walletRefundedAmount);
         if (walletCanUseAmount.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal walletAmount;
            if (walletCanUseAmount.compareTo(remainingAmount) <= 0) {
               walletAmount = walletCanUseAmount;
               remainingAmount = remainingAmount.subtract(walletCanUseAmount);
            } else {
               walletAmount = remainingAmount;
               remainingAmount = BigDecimal.ZERO;
            }

            AccWalletBalanceUpdatePO rechargePo = (new AccWalletBalanceUpdatePO()).setOperationType(AccUpdateWalletOperateEnum.AUGMENT_BAL).setCustId(accInfoVO.getCustId()).setWalletId(walletId).setAmount(walletAmount);
            AccUpdateWalletBalanceVO accUpdateWalletBalanceVO = this.accWalletInfoService.updateAccWalletBalance(rechargePo);
            AccTradeWalletPO accTradeWalletPo = AccTradeWalletPO.builder().tradeId(tradeId).custId(accInfoVO.getCustId()).walletId(walletId).tradeType(tradeType).amount(walletAmount).walletBal(AccUtils.getWalletBal(accUpdateWalletBalanceVO.getWalletInfoList(), walletId)).frozenBalance(AccUtils.getWalletFrozenBal(accUpdateWalletBalanceVO.getWalletInfoList(), walletId)).tradeTime(now).build();
            this.accTradeWalletDetailService.insertAccTradeWalletDetail(accTradeWalletPo);
            walletRefundPayList.add(AccWalletPayPO.builder().walletId(walletId).custId(accInfoVO.getCustId()).amount(walletAmount).walletBal(AccUtils.getWalletBal(accUpdateWalletBalanceVO.getWalletInfoList(), walletId)).build());
            if (remainingAmount.compareTo(BigDecimal.ZERO) == 0) {
               break;
            }
         }
      }

      AccUpdateWalletBalanceVO accWalletBalance = this.accWalletInfoService.getAccWalletBalanceByCustId(accInfoVO.getCustId());
      AccTradeAddPO accTradeAddPo = AccTradeAddPO.builder().tradeId(tradeId).tradeType(tradeType).tradeTime(now).custId(accInfoVO.getCustId()).psnType(accInfoVO.getPsnType()).orgId(accInfoVO.getOrgId()).actualAmount(accOrdRefundDTO.getAmount()).amount(accOrdRefundDTO.getAmount()).walletBalTotal(accWalletBalance.getAccBalTotal()).accAllBal(accWalletBalance.getAccAllBal()).payChannel(PayChannelEnum.ACC.getKey()).payType(PayTypeEnum.MEAL_CARD.getKey()).payState(PayStateEnum.PAY_SUCC.getKey()).tradeState(AccTradeStateEnum.TAKE_EFFECT.getKey()).leOrdNo(accOrdRefundDTO.getRefundOrderId()).originTradeId(originTradeId).crby(AccUtils.getOperatorName()).build();
      this.accTradeService.insertAccTradeForTradeId(accTradeAddPo);
      log.info("[退款订单]->修改钱包,新增记录结束");
      return tradeId;
   }

   protected AccInfoVO getAccInfoByCustId(Long custId) {
      AccInfoVO accInfoVO = this.accInfoService.getAccInfoVoByCustId(custId);
      this.accInfoService.checkAccInfoAndStatus(accInfoVO);
      return accInfoVO;
   }

   protected List<Integer> getWalletPayIdList(AccOrdBaseDTO accOrdOnlineDTO) {
      RulePayComputeDTO rulePayComputeDTO = new RulePayComputeDTO(accOrdOnlineDTO);
      RulePayResultDTO rulePayResultDTO = this.marketApi.rulePayCompute(rulePayComputeDTO);
      return CollUtil.isNotEmpty(rulePayResultDTO.getWalletIds()) ? rulePayResultDTO.getWalletIds() : this.getWalletPayDefaultOrder();
   }

   public List<Integer> getWalletPayDefaultOrder() {
      List<Integer> list = new ArrayList();
      AccMetadataModel accMetadataModel = this.accMetadataService.queryMetadata();
      String accPayOrder;
      if (ObjectUtil.isAllNotEmpty(new Object[]{accMetadataModel, accMetadataModel.getAccPayOrder()})) {
         accPayOrder = accMetadataModel.getAccPayOrder();
         log.info("获取配置项扣款优先级:{}", JSONUtil.toJsonStr(accPayOrder));
      } else {
         accPayOrder = AccWalletIdEnum.getAllWalletId();
         log.info("获取默认扣款优先级:{}", JSONUtil.toJsonStr(accPayOrder));
      }

      String[] walletIds = accPayOrder.split(",");
      String[] var5 = walletIds;
      int var6 = walletIds.length;

      for(int var7 = 0; var7 < var6; ++var7) {
         String walletId = var5[var7];
         list.add(Integer.valueOf(walletId));
      }

      return list;
   }

   protected void addUnpaidBalanceAccPay(AccInfoVO accInfoVO, List<AccWalletPayPO> walletPayList, BigDecimal unpaidBalance) {
      Map<Integer, AccWalletPayPO> walletPayMap = (Map)walletPayList.stream().collect(Collectors.toMap(AccWalletPayPO::getWalletId, Function.identity()));
      AccMetadataModel accMetadataModel = this.accMetadataService.queryMetadata();
      log.info("获取账户配置项：{}", JSONUtil.toJsonStr(accMetadataModel));
      Integer walletId = AccWalletIdEnum.WALLET.getKey();
      if (ObjectUtil.isNotNull(accMetadataModel) && CharSequenceUtil.isNotBlank(accMetadataModel.getOfflineWalletPay())) {
         AccWalletIdEnum walletIdEnum = AccWalletIdEnum.getEnum(Integer.valueOf(accMetadataModel.getOfflineWalletPay()));
         if (ObjectUtil.isNotNull(walletIdEnum)) {
            walletId = walletIdEnum.getKey();
         }
      }

      AccWalletPayPO walletPay = (AccWalletPayPO)walletPayMap.get(walletId);
      if (ObjectUtil.isNotNull(walletPay)) {
         walletPay.setWalletBal(walletPay.getWalletBal().subtract(unpaidBalance));
         walletPay.setAmount(walletPay.getAmount().add(unpaidBalance));
      } else {
         walletPay = new AccWalletPayPO();
         walletPay.setWalletId(walletId);
         BigDecimal walletBal = AccUtils.getWalletBal(accInfoVO.getWalletInfoList(), walletId);
         walletPay.setWalletBal(walletBal.subtract(unpaidBalance));
         walletPay.setAmount(unpaidBalance);
         walletPayList.add(walletPay);
      }

   }

   protected AccOrdBasicsVO assembledAccOrdTradeDetail(Long custId, Long accTradeId, List<AccWalletPayPO> payWalletlist) {
      AccInfoVO newAccInfoVO = this.accInfoService.getAccInfoVoByCustId(custId);
      log.info("账户支付完成,重新获取账户信息:{}", JSONUtil.toJsonStr(newAccInfoVO));
      return AccUtils.transformAccOrdTradeDetailV2(newAccInfoVO, accTradeId, payWalletlist);
   }

   protected AccOrdBasicsVO assembledTrialCalculation(Long custId, List<AccWalletPayPO> payWalletlist) {
      AccInfoVO newAccInfoVO = this.accInfoService.getAccInfoVoByCustId(custId);
      log.info("账户试算支付完成,重新获取账户信息:{}", JSONUtil.toJsonStr(newAccInfoVO));
      return AccUtils.transformAccOrdTradeDetailV4(newAccInfoVO, payWalletlist);
   }

   protected AccOrdBasicsVO assembledAccOrdTradeDetailV2(AccConsumeWalletVO accConsumeWalletVO, List<AccWalletPayPO> payWalletList) {
      return AccUtils.transformAccOrdTradeDetailV3(accConsumeWalletVO.getAccTradeId(), accConsumeWalletVO.getWalletInfoList(), payWalletList);
   }
}
