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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.constant.LeConstants;
import com.bonus.core.account.v3.app.dto.AccRechargeForAppDTO;
import com.bonus.core.account.v3.app.dto.AppRechargeResultDTO;
import com.bonus.core.account.v3.app.dto.AppTradeDetailListDTO;
import com.bonus.core.account.v3.app.service.AppAccService;
import com.bonus.core.account.v3.app.vo.AppAccTradePageVO;
import com.bonus.core.account.v3.app.vo.AppRechargeResultVO;
import com.bonus.core.account.v3.constants.AccTradeStateEnum;
import com.bonus.core.account.v3.constants.AccTradeTypeEnum;
import com.bonus.core.account.v3.model.AccTrade;
import com.bonus.core.account.v3.po.AccTradeAddPO;
import com.bonus.core.account.v3.service.AccInfoService;
import com.bonus.core.account.v3.service.AccTradeService;
import com.bonus.core.account.v3.service.AccWalletService;
import com.bonus.core.account.v3.web.dto.AccRechargeBackDTO;
import com.bonus.core.account.v3.web.vo.AccInfoVO;
import com.bonus.core.allocation.api.AllocMetadataApi;
import com.bonus.core.common.enums.MetadataModelTypeEnum;
import com.bonus.core.customer.api.CustInfoApi;
import com.bonus.core.customer.constants.CustConstant;
import com.bonus.core.customer.dto.CustInfoDTO;
import com.bonus.core.customer.vo.CustInfoVo;
import com.bonus.core.marketing.v2.api.MarketApi;
import com.bonus.core.marketing.v2.rule.recharge.dto.RuleRechargeComputeDTO;
import com.bonus.core.pay.api.PayApi;
import com.bonus.core.pay.api.dto.UnifyPayDTO;
import com.bonus.core.pay.api.dto.UnifyPaySelectDTO;
import com.bonus.core.pay.api.vo.UnifyPaySelectVO;
import com.bonus.core.pay.common.constants.PayChannelSelectEnum;
import com.bonus.core.pay.common.constants.PayStateEnum;
import com.bonus.core.pay.common.constants.TradeTypeEnum;
import com.bonus.core.pay.common.vo.MobilePayVO;
import com.bonus.core.pay.common.vo.PayChannelSelectDTO;
import com.bonus.core.pay.common.vo.PayChannelSelectResultVO;
import com.bonus.utils.id.Id;
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.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

@Service
public class AppAccServiceImpl implements AppAccService {
   private static final Logger log = LoggerFactory.getLogger(AppAccServiceImpl.class);
   @Resource
   @Lazy
   private AccTradeService accTradeService;
   @Resource
   @Lazy
   private AccWalletService accWalletService;
   @Resource
   @Lazy
   private CustInfoApi custInfoApi;
   @Resource
   @Lazy
   private AllocMetadataApi allocMetadataApi;

   @Resource
   @Lazy
   private PayApi payApi;
   @Resource
   @Lazy
   private AccInfoService accInfoService;
   @Resource
   @Lazy
   private MarketApi marketApi;


   @Override
   public Page<AppAccTradePageVO>  queryTradeAppPage(AppTradeDetailListDTO dto) {
      dto.setTradeTypeList(AccTradeTypeEnum.getRechargeListForApp());
      dto.setPayStateList(dto.convertPayStateList());
      return this.accTradeService.queryTradeAppPage(dto);
   }
   @Override
   @Transactional(
           rollbackFor = {Exception.class}
   )
   public MobilePayVO accRechargeForApp(AccRechargeForAppDTO dto) {
      log.info("[移动端充值]入参_{}", JSONUtil.toJsonStr(dto));
      if (ObjectUtil.isNull(dto.getCustId())) {
         throw new ServiceException("账户ID不能为空");
      } else {
         CustInfoVo custInfoVo = this.custInfoApi.queryCustInfo((new CustInfoDTO()).setCustId(dto.getCustId()));
         if (ObjectUtil.isNull(custInfoVo)) {
            throw new ServiceException("账户不存在");
         } else {
            String metadataValue = this.allocMetadataApi.getMetadataValue(MetadataModelTypeEnum.MOBILE, "ifAllowTouristsToTopUp");
            log.info("[移动端充值]是否允许游客充值配置项:{}", metadataValue);
            if (StrUtil.isNotBlank(metadataValue) && LeConstants.COMMON_NO.equals(Integer.valueOf(metadataValue)) && ObjectUtil.isNotNull(custInfoVo.getPsnType()) && CustConstant.TEMPORARY_PSN_TYPE.equals(custInfoVo.getPsnType())) {
               log.info("[移动端充值]游客不支持充值:{}", custInfoVo.getPsnType());
               throw new ServiceException("游客不支持充值");
            } else {
               return this.appRechargeCommonHandle(dto, custInfoVo);
            }
         }
      }
   }

   protected MobilePayVO appRechargeCommonHandle(AccRechargeForAppDTO dto, CustInfoVo custInfoVo) {
      PayChannelSelectResultVO payChannelResultVO = this.payTypeAndTradeTypeHandler(dto);
      AccInfoVO accInfo = this.accInfoService.getAccInfoVoByCustId(custInfoVo.getCustId());
      this.accInfoService.checkAccInfoAndStatus(accInfo);
      AccTradeAddPO accPo = AccTradeAddPO.builder().tradeTime(LocalDateTime.now()).custId(custInfoVo.getCustId()).tradeType(AccTradeTypeEnum.RECHARGE.getKey()).amount(dto.getRechargeAmount()).payChannel(payChannelResultVO.getPayChannel()).payType(payChannelResultVO.getPayType()).payState(PayStateEnum.PAY_INPROCESS.getKey()).tradeState(AccTradeStateEnum.CREATE.getKey()).rechargeSource(dto.getSourceType()).build();
      Long accTradeId = this.accTradeService.insertAccTrade(accPo);
      this.checkRechargeRule(custInfoVo.getCustId(), accTradeId, dto.getRechargeAmount(), payChannelResultVO);
      UnifyPayDTO mobilePayDTO = this.appPayParamDTO(custInfoVo, dto, accTradeId);
      mobilePayDTO.setPayChannel(accPo.getPayChannel());
      mobilePayDTO.setPayChannelSplits(payChannelResultVO.getPayChannelSplits());
      log.info("[移动端充值]调用支付dto_{}", JSONUtil.toJsonStr(mobilePayDTO));
      MobilePayVO mobilePayVO = this.payApi.pay(mobilePayDTO).convertToMobilePayVO();
      mobilePayVO.setAccTradeId(accTradeId);
      log.info("[移动端充值]_拉起支付返回值:{}", JSONUtil.toJsonStr(mobilePayVO));
      this.mobileRechargeResultHandler(accTradeId, mobilePayVO);
      return mobilePayVO;
   }

   protected PayChannelSelectResultVO payTypeAndTradeTypeHandler(AccRechargeForAppDTO dto) {
      PayChannelSelectDTO payChannelSelectDTO = new PayChannelSelectDTO();
      payChannelSelectDTO.setPayType(dto.getPayType());
      payChannelSelectDTO.setSourceType(dto.getSourceType());
      return this.payApi.payChannelSelect(PayChannelSelectEnum.MOBILE_RECHARGE, payChannelSelectDTO);
   }


   protected void checkRechargeRule(Long custId, Long accTradeId, BigDecimal amount, PayChannelSelectResultVO payChannelResultVO) {
      this.marketApi.ruleRechargeCompute((new RuleRechargeComputeDTO()).setCustId(custId).setAmount(amount).setPayChannel(payChannelResultVO.getPayChannel()).setPayType(payChannelResultVO.getPayType()).setTradeId(accTradeId).setTradeDate(LocalDate.now()));
   }

   protected void mobileRechargeResultHandler(Long accTradeId, MobilePayVO mobilePayVO) {
      if (PayStateEnum.isFailedState(mobilePayVO.getCode())) {
         AccTrade updateTrade = new AccTrade();
         updateTrade.setId(accTradeId);
         updateTrade.setPayState(mobilePayVO.getCode());
         updateTrade.setTradeState(AccTradeStateEnum.CLOSE.getKey());
         updateTrade.setFailReason(mobilePayVO.getMsg());
         this.accTradeService.updateTradeById(updateTrade);
      }

   }

   protected UnifyPayDTO appPayParamDTO(CustInfoVo custInfoVo, AccRechargeForAppDTO dto, Long rechargeId) {
      UnifyPayDTO mobilePayDTO = new UnifyPayDTO();
      mobilePayDTO.setCustId(custInfoVo.getCustId());
      mobilePayDTO.setCustName(custInfoVo.getCustName());
      mobilePayDTO.setAmount(dto.getRechargeAmount());
      mobilePayDTO.setOpenid(dto.getOpenid());
      mobilePayDTO.setPayType(dto.getPayType());
      mobilePayDTO.setSourceType(dto.getSourceType());
      mobilePayDTO.setTradeType(TradeTypeEnum.RECHARGE.getKey());
      mobilePayDTO.setTradeId(Id.next());
      mobilePayDTO.setScenario(PayChannelSelectEnum.MOBILE_RECHARGE.getKey());
      UnifyPayDTO.Order order = new UnifyPayDTO.Order();
      order.setOrderId(rechargeId);
      order.setAmount(dto.getRechargeAmount());
      order.setOrderTime(LocalDateTime.now());
      mobilePayDTO.setOrderList(CollUtil.toList(new UnifyPayDTO.Order[]{order}));
      mobilePayDTO.setPayPassword(dto.getPayPassword());
      return mobilePayDTO;
   }

   @Override
   public AppAccTradePageVO queryAppRechargePayOvertimeResult(AppRechargeResultDTO content) {
      UnifyPaySelectDTO paySelectDTO = new UnifyPaySelectDTO();
      paySelectDTO.setOrderRechargeId(content.getAccTradeId());
      log.info("移动端查询充值结果,调用支付查询入参_{}", JSONUtil.toJsonStr(paySelectDTO));
      UnifyPaySelectVO paySelectVO = this.payApi.paySelect(paySelectDTO);
      log.info("移动端查询充值结果,调用支付查询结果_{}", JSONUtil.toJsonStr(paySelectVO));
      this.accWalletService.accRechargeResultHandlerV2(AccRechargeBackDTO.convert(content.getAccTradeId(), paySelectVO));
      return this.accTradeService.getOneTradeApp(content.getAccTradeId());
   }

   public AppRechargeResultVO queryAppRechargeResult(AppRechargeResultDTO content) {
      log.info("移动端查询充值结果,入参_{}", JSONUtil.toJsonStr(content));
      AccTrade accTrade = this.accTradeService.getOne(Wrappers.lambdaQuery(AccTrade.class)
              .select(AccTrade::getId, AccTrade::getPayState, AccTrade::getRemark
                      , AccTrade::getFailReason).eq(AccTrade::getId, content.getAccTradeId()));
      log.info("移动端查询充值结果,出参_{}", JSONUtil.toJsonStr(accTrade));
      String remark = PayStateEnum.isFailedState(accTrade.getPayState()) ? accTrade.getFailReason() : accTrade.getRemark();
      return (new AppRechargeResultVO()).setTradeId(accTrade.getId()).setPayState(accTrade.getPayState()).setRemark(remark);
   }

}
