package com.hzs.member.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzs.common.core.constant.MagicNumberConstants;
import com.hzs.common.core.constant.MemberFieldConstants;
import com.hzs.common.core.constant.msg.FinanceMsgConstants;
import com.hzs.common.core.domain.R;
import com.hzs.common.core.enums.*;
import com.hzs.common.core.utils.CommonUtil;
import com.hzs.common.core.utils.ComputeUtil;
import com.hzs.common.core.utils.DateUtils;
import com.hzs.common.core.utils.reflect.ReflectUtils;
import com.hzs.common.domain.member.account.*;
import com.hzs.common.domain.member.base.CuMember;
import com.hzs.common.domain.member.ext.CuMemberAccountExt;
import com.hzs.common.domain.sale.ext.BdWaresInfoExt;
import com.hzs.common.domain.sale.order.SaOrder;
import com.hzs.common.domain.sale.order.SaOrderItems;
import com.hzs.common.domain.system.config.BdAccount;
import com.hzs.common.domain.system.sys.SysUser;
import com.hzs.member.account.dto.*;
import com.hzs.member.account.mapper.CuMemberRechargeDetailMapper;
import com.hzs.member.account.mapper.CuMemberRechargeMapper;
import com.hzs.member.account.service.*;
import com.hzs.member.account.vo.CuMemberPetitionListVO;
import com.hzs.member.account.vo.CuMemberPetitionVO;
import com.hzs.member.account.vo.CuMemberRechargeDetailVO;
import com.hzs.member.account.vo.CuMemberRechargeVO;
import com.hzs.member.achieve.service.ICuMemberSettlePeriodService;
import com.hzs.member.base.service.ICuMemberService;
import com.hzs.sale.order.ISaOrderServiceApi;
import com.hzs.system.base.dto.CurrencyDTO;
import com.hzs.system.config.IAccountServiceApi;
import com.hzs.system.config.dto.AreaCurrencyDTO;
import com.hzs.system.sys.IApprovalServiceApi;
import com.hzs.system.sys.dto.ApprovalBusinessResultDTO;
import com.hzs.system.sys.dto.ApprovalInfoDTO;
import com.hzs.system.sys.dto.ApprovalSubmitDTO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 会员充值表 服务实现类
 */
@Service
public class CuMemberRechargeServiceImpl extends ServiceImpl<CuMemberRechargeMapper, CuMemberRecharge> implements ICuMemberRechargeService {

    @Autowired
    ICuMemberService cuMemberService;
    @Autowired
    ICuMemberTradeService cuMemberTradeService;
    @Autowired
    ICuMemberAccountService cuMemberAccountService;
    @Autowired
    ICuMemberBaseService cuMemberBaseService;
    @Autowired
    ICuMemberSettlePeriodService cuMemberSettlePeriodService;
    @Autowired
    CuMemberRechargeDetailMapper cuMemberRechargeDetailMapper;
    @Autowired
    ICuMemberTradeDetailService cuMemberTradeDetailService;
    @Autowired
    ICuMemberTradeBonusService cuMemberTradeBonusService;
    @Autowired
    ICuMemberRedeemPointsService cuMemberRedeemPointsService;

    @DubboReference
    IApprovalServiceApi approvalServiceApi;
    @DubboReference
    IAccountServiceApi accountServiceApi;
    @DubboReference
    ISaOrderServiceApi saOrderServiceApi;


    @Override
    public List<CuMemberRecharge> selectRechargeByBatch(String batch) {
        //根据单号查询充值信息
        QueryWrapper<CuMemberRecharge> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("BATCH", batch);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer saveBatchMemberRecharge(CuMemberBatchRechargeParam batchRechargeParam) {
        //批次号
        String batchNum = CommonUtil.createSerialNumber(EOrderPrefix.RECHARGE_CODE.getValue());
        batchRechargeParam.setBatchNum(batchNum);
        List<CuMemberRecharge> cuMemberRecharges = new ArrayList<>();
        List<CuMemberRechargeDetail> cuMemberRechargeDetails = new ArrayList<>();
        //充值详情参数
        List<CuMemberBatchRechargeDetailParam> detailParams = batchRechargeParam.getDetailParams();
        if (CollUtil.isNotEmpty(detailParams)) {
            //会员编号集合
            List<String> memberCodes = batchRechargeParam.getMemberCodes();
            //查询账户余额
            List<CuMemberAccountExt> cuMemberAccounts = cuMemberAccountService.queryMemberAccountByMemberCodes(new ArrayList<>(), memberCodes);
            Map<String, CuMemberAccountExt> memberAccountExtMap = cuMemberAccounts.stream().collect(Collectors.toMap(CuMemberAccountExt::getMemberCode, o -> o));
            batchRechargeParam.setMemberAccountExtMap(memberAccountExtMap);
            for (CuMemberBatchRechargeDetailParam detailParam : detailParams) {
                //每个编号的充值信息
                Object[] objects = getMemberRechargeInfo(batchRechargeParam, detailParam);
                cuMemberRecharges.addAll((List<CuMemberRecharge>) objects[0]);
                cuMemberRechargeDetails.addAll((List<CuMemberRechargeDetail>) objects[1]);
            }
        }
        //批量插入充值详情表
        cuMemberRechargeDetailMapper.batchSaveMemberRechargeDetail(cuMemberRechargeDetails);
        //批量插入充值表
        Integer row = baseMapper.batchSaveAccountByMemberCode(cuMemberRecharges);
        //提交审批
        submitBatchApproval(batchRechargeParam, batchNum);
        return row;
    }

    /**
     * 提交审批
     *
     * @param batchRechargeParam 充值信息
     */
    private void submitBatchApproval(CuMemberBatchRechargeParam batchRechargeParam, String batchNum) {
        //调用审批接口，修改审批状态
        List<CuMember> cuMembers = batchRechargeParam.getCuMembers();
        List<Long> pkMembers = cuMembers.stream().map(CuMember::getPkId).collect(Collectors.toList());
        Set<Long> pkMemberIds = CollUtil.newHashSet(pkMembers);

        ApprovalSubmitDTO approvalSubmitDTO = ApprovalSubmitDTO.builder().eApprovalBusiness(EApprovalBusiness.RECHARGE)
                .businessCode(batchNum).businessData(JSONUtil.toJsonStr(batchRechargeParam))
                .signType(ESignType.getEnumByValue(batchRechargeParam.getSignType())).userIdList(batchRechargeParam.getUserIdList())
                .sendIdList(batchRechargeParam.getSendIdList()).remark(batchRechargeParam.getRemark()).pkMemberList(pkMemberIds).build();
        R<String> submit = approvalServiceApi.submit(approvalSubmitDTO, batchRechargeParam.getLoginUser());
        if (!submit.isSuccess()) {
            throw new RuntimeException(submit.getMsg());
        }
    }

    /**
     * 获得用户的批量充值信息
     *
     * @param batchRechargeParam 充值入参
     * @param detailParam        充值详情入参
     * @return Object[]
     */

    private Object[] getMemberRechargeInfo(CuMemberBatchRechargeParam batchRechargeParam, CuMemberBatchRechargeDetailParam detailParam) {
        //避免充值单号的重复
        int j = 0;
        CuMemberRecharge memberRecharge;
        CuMemberRechargeDetail cuMemberRechargeDetail;
        List<CuMemberRecharge> memberRecharges = new ArrayList<>();
        List<CuMemberRechargeDetail> memberRechargeDetails = new ArrayList<>();
        //币种类型、金额
        Map<String, BigDecimal> rechargeDetailMap = detailParam.getRechargeDetailMap();
        for (Map.Entry<String, BigDecimal> entry : rechargeDetailMap.entrySet()) {
            j++;
            //充值单号
            String rechargeCode = CommonUtil.createSerialNumber(EOrderPrefix.RECHARGE_CODE.getValue());
            rechargeCode = rechargeCode + j;
            Integer pkAccount = Integer.parseInt(entry.getKey());
            BigDecimal paymentAmount = entry.getValue();
            if (null != paymentAmount && paymentAmount.compareTo(BigDecimal.ZERO) >= 0) {
                //计算充值金额、余额
                BigDecimal[] rechargeAmountBalance = getBatchRechargeAmount(batchRechargeParam, detailParam, paymentAmount, pkAccount);
                memberRecharge = packageCuMemberBatchRecharge(batchRechargeParam, detailParam, rechargeCode, pkAccount, rechargeAmountBalance[0]);
                cuMemberRechargeDetail = packageCuMemberBatchRechargeDetail(batchRechargeParam, detailParam, rechargeCode, rechargeAmountBalance);
                memberRecharges.add(memberRecharge);
                memberRechargeDetails.add(cuMemberRechargeDetail);
            }
        }
        return new Object[]{memberRecharges, memberRechargeDetails};
    }

    /**
     * 拼装批量充值信息详情
     *
     * @param batchRechargeParam    充值入参
     * @param detailParam           充值详情入参
     * @param rechargeCode          充值单号
     * @param rechargeAmountBalance 充值金额、余额
     * @return CuMemberRechargeDetail
     */
    private CuMemberRechargeDetail packageCuMemberBatchRechargeDetail(CuMemberBatchRechargeParam batchRechargeParam,
                                                                      CuMemberBatchRechargeDetailParam detailParam,
                                                                      String rechargeCode,
                                                                      BigDecimal[] rechargeAmountBalance) {
        BigDecimal rechargeAmount = rechargeAmountBalance[0];
        BigDecimal balance = rechargeAmountBalance[1];
        CuMemberRechargeDetail detail = CuMemberRechargeDetail.builder()
                .rechargeCode(rechargeCode).paymentMethod(detailParam.getPaymentMethod())
                .paymentAmount(rechargeAmount)
                .paymentBeforeBalance(balance)
                .paymentBalance(balance.add(rechargeAmount))
                .build();
        detail.setPkCreator(batchRechargeParam.getLoginUser().getUserId());
        return detail;
    }

    /**
     * 获取批理充值金额、余额
     *
     * @param batchRechargeParam 充值入参
     * @param detailParam        充值详情入参
     * @param paymentAmount      充值金额
     * @param pkAccount          充值账户
     * @return
     */
    private BigDecimal[] getBatchRechargeAmount(CuMemberBatchRechargeParam batchRechargeParam,
                                                CuMemberBatchRechargeDetailParam detailParam,
                                                BigDecimal paymentAmount, Integer pkAccount) {
        String memberCode = detailParam.getMemberCode();
        //账户余额
        Map<String, CuMemberAccountExt> memberAccountExtMap = batchRechargeParam.getMemberAccountExtMap();
        BigDecimal balance = ReflectUtils.invokeGetter(memberAccountExtMap.get(memberCode), MemberFieldConstants.ACCOUNT + cuMemberBaseService.getAccountValueById(pkAccount));
        //充值方式
        Integer rechargeMethod = batchRechargeParam.getRechargeMethod();
        return new BigDecimal[]{getAmountByRechargeMethod(rechargeMethod, paymentAmount, balance), balance};
    }

    /**
     * 拼装批量充值信息
     *
     * @param batchRechargeParam 充值入参
     * @param detailParam        充值详情入参
     * @param rechargeCode       充值单号
     * @param pkAccount          账号类型ID
     * @param paymentAmount      充值金额
     * @return CuMemberRecharge
     */
    private CuMemberRecharge packageCuMemberBatchRecharge(CuMemberBatchRechargeParam batchRechargeParam,
                                                          CuMemberBatchRechargeDetailParam detailParam,
                                                          String rechargeCode, Integer pkAccount,
                                                          BigDecimal paymentAmount) {
        String memberCode = detailParam.getMemberCode();
        Long pkMember = batchRechargeParam.getMemberMap().get(memberCode);
        CuMemberRecharge memberRecharge = CuMemberRecharge.builder().pkMember(pkMember)
                .rechargeAmount(paymentAmount)
                .pkAccount(pkAccount).rechargeType(detailParam.getPaymentMethod().toString())
                .rechargeCode(rechargeCode)
                .rechargeMethod(batchRechargeParam.getRechargeMethod())
                .remarks(detailParam.getRemarks())
                .memberRemarks(detailParam.getMemberRemarks())
                .batch(batchRechargeParam.getBatchNum())
                .build();
        //审核状态为已提交
        memberRecharge.setApproveState(EApproveRechargeStatus.WAIT.getValue());
        memberRecharge.setPkCountry(batchRechargeParam.getPkCountry());
        memberRecharge.setPkCreator(batchRechargeParam.getLoginUser().getUserId());
        memberRecharge.setCreationTime(new Date());
        memberRecharge.setPkRate(batchRechargeParam.getPkRate());
        return memberRecharge;
    }


    /**
     * 根据充值方式计算充值金额
     *
     * @param rechargeMethod 充值方式
     * @param amount         金额
     * @param balance        余额
     * @return BigDecimal
     */
    private BigDecimal getAmountByRechargeMethod(Integer rechargeMethod, BigDecimal amount, BigDecimal balance) {
        BigDecimal currentAmount;
        //减少
        if (rechargeMethod.equals(ERechargeMethod.BANK_TRANSFER.getValue())) {
            currentAmount = amount.negate();
        }
        //最终金额
        else if (rechargeMethod.equals(ERechargeMethod.FULL_AMOUNT.getValue())) {
            currentAmount = amount.subtract(balance);
        } else {
            currentAmount = amount;
        }
        return currentAmount;
    }

    /**
     * 拼装充值信息
     *
     * @param cuMemberRechargeAccountParam 充值币种信息
     * @param cuMemberRechargeParam        会员充值
     * @param rechargeCode                 充值单号
     * @return CuMemberRecharge
     */
    private CuMemberRecharge packageCuMemberRecharge(CuMemberRechargeAccountParam cuMemberRechargeAccountParam,
                                                     CuMemberRechargeParam cuMemberRechargeParam, String rechargeCode) {
        CuMemberRecharge memberRecharge = CuMemberRecharge.builder().pkMember(cuMemberRechargeParam.getPkMember())
                .pkAccount(cuMemberRechargeAccountParam.getAccountType()).rechargeCode(rechargeCode)
                .rechargeMethod(cuMemberRechargeParam.getRechargeMethod())
                .remarks(cuMemberRechargeAccountParam.getRemarks())
                .build();
        //审核状态为已提交
        memberRecharge.setApproveState(EApproveRechargeStatus.WAIT.getValue());
        memberRecharge.setPkCountry(cuMemberRechargeParam.getPkCountry());
        memberRecharge.setPkCreator(cuMemberRechargeParam.getLoginUser().getUserId());
        memberRecharge.setCreationTime(new Date());
        memberRecharge.setPkRate(cuMemberRechargeParam.getPkRate());
        return memberRecharge;
    }


    @Override
    public Integer updateBathRechargeByRechargeCode(MemberAccountAudit memberAccountAudit) {
        CuMemberRecharge cuMemberRecharge = new CuMemberRecharge();
        BeanUtils.copyProperties(memberAccountAudit, cuMemberRecharge);
        cuMemberRecharge.setRechargeCode(memberAccountAudit.getBusinessCode());
        return baseMapper.updateBathRechargeByRechargeCode(cuMemberRecharge);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer submitAuditMemberRecharge(MemberAccountAudit memberAccountAudit) {
        return this.updateBathRechargeByRechargeCode(memberAccountAudit);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMemberTradeAndMemberBalance(ApprovalBusinessResultDTO approvalBusinessResultDTO, List<CuMemberRecharge> cuMemberRecharges) {
        CuMemberTrade memberTrade;
        List<CuMemberTrade> cuMemberTrades = new ArrayList<>();
        String batch = approvalBusinessResultDTO.getBusinessCode();
        Long userId = approvalBusinessResultDTO.getLoginUser().getUserId();
        for (CuMemberRecharge cuMemberRecharge : cuMemberRecharges) {
            memberTrade = this.getCuMemberTrade(batch, cuMemberRecharge);
            memberTrade.setPkCreator(userId);
            cuMemberTrades.add(memberTrade);
        }
        //批量插入流水表
        cuMemberTradeService.batchSaveTradeByMemberCode(cuMemberTrades);
        //批理更新余额
        cuMemberAccountService.updateBatchMemberAccountByPkmember(batch, userId, ETransferType.TRANSFER_IN.getValue());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMemberTradeDetailAndBonus(List<CuMemberAccount> memberAccountList, Integer rechargeMethod, String businessCode) {
        //充值为正时插入奖金余额表
        if (rechargeMethod.equals(ERechargeMethod.BANK_TRANSFER.getValue())) {
            //查询充值奖金币的充值信息
            List<CuMemberRecharge> cuMemberRecharges = baseMapper.selectMemberRechargeBonusBySourceCode(businessCode);
            Map<Long, Long> idsMap = cuMemberRecharges.stream().collect(Collectors.toMap(CuMemberRecharge::getPkMember, CuMemberRecharge::getPkId));
            cuMemberBaseService.handleMemberTradeDetailAndBonus(memberAccountList, businessCode, idsMap, EConsumeType.RECHARGE.getValue());
        } else {
            cuMemberBaseService.saveMemberTradeDetail(businessCode);
        }
    }

    /**
     * 构造交易明细
     *
     * @param cuMemberRecharge 充值信息
     * @return CuMemberTrade
     */
    private CuMemberTrade getCuMemberTrade(String batch, CuMemberRecharge cuMemberRecharge) {
        Long pkMember = cuMemberRecharge.getPkMember();
        int pkAccount = cuMemberRecharge.getPkAccount();
        BdAccount account = cuMemberBaseService.getAccount(pkAccount);
        //充值金额
        BigDecimal tradeAmount = cuMemberRecharge.getRechargeAmount();
        //查询余额
        BigDecimal balance = cuMemberAccountService.selectBalanceByMemberCode(pkMember, account.getFieldValue());
        CuMemberTrade memberTrade = CuMemberTrade.builder().pkMember(pkMember)
                .tradeCode(cuMemberRecharge.getRechargeCode())
                .pkAccount(pkAccount)
                .tradeType(ETradeType.OFF_LINE_RECHARGE.getValue())
                .tradeClassify(ETradeClassify.RECHARGE.getValue())
                .pkRate(cuMemberRecharge.getPkRate())
                .tradeAmount(tradeAmount)
                .tradeBeforeBalance(balance)
                .tradeBalance(balance.add(tradeAmount))
                .sourceCode(batch)
                .tradeStatue(ETradeStatus.TAKE_EFFECT.getValue())
                .remarks(cuMemberRecharge.getRemarks())
                .build();
        memberTrade.setPkCountry(cuMemberRecharge.getPkCountry());
        return memberTrade;
    }


    @Override
    public CuMemberRechargeDetailVO queryCuMemberRechargeDetail(Long pkId) {
        // TODO: 2022/11/9  详情功能暂无
        CuMemberRechargeDetailVO memberRechargeDetailVo = new CuMemberRechargeDetailVO();
        CuMemberRechargeExt memberRecharge = baseMapper.selectMemberRechargeById(pkId);
        BeanUtils.copyProperties(memberRecharge, memberRechargeDetailVo);

        return memberRechargeDetailVo;
    }

    @Override
    public Object[] findRechargeList(CuMemberRechargeVO memberRechargeVO) {
        CuMemberRechargeVO cuMemberRechargeVO;
        List<CuMemberRechargeVO> cuMemberRechargeVOS = new ArrayList<>();
        List<CuMemberRechargeExt> cuMemberRechargeExts = baseMapper.selectMemberRechargeList(memberRechargeVO);
        if (CollUtil.isNotEmpty(cuMemberRechargeExts)) {
            for (CuMemberRechargeExt cuMemberRechargeExt : cuMemberRechargeExts) {
                cuMemberRechargeVO = new CuMemberRechargeVO();
                BeanUtils.copyProperties(cuMemberRechargeExt, cuMemberRechargeVO);
                cuMemberRechargeVO = setPublicParam(cuMemberRechargeVO);
                cuMemberRechargeVOS.add(cuMemberRechargeVO);
            }
            CuMemberRechargeExt cuMemberRechargeExt = baseMapper.selectMemberRechargeListTotal(memberRechargeVO);
            cuMemberRechargeVOS.add(BeanUtil.copyProperties(cuMemberRechargeExt, CuMemberRechargeVO.class));
        }
        Object[] objects = new Object[]{cuMemberRechargeExts, cuMemberRechargeVOS};
        return objects;
    }

    /**
     * 页面显示的公用参数
     *
     * @param cuMemberRechargeVO VO
     * @return Object
     */
    private CuMemberRechargeVO setPublicParam(CuMemberRechargeVO cuMemberRechargeVO) {
        //翻译值
        Integer pkAccount = cuMemberRechargeVO.getPkAccount();
        cuMemberRechargeVO.setPkAccount(cuMemberBaseService.translateAccountValue(pkAccount));
        //汇率
        BigDecimal exchangeRate = cuMemberBaseService.getCurrencyById(cuMemberRechargeVO.getPkRate()).getInExchangeRate();
        //充值本币
        BigDecimal settleRechargeAmount = cuMemberRechargeVO.getPaymentAmount().divide(exchangeRate, 4, RoundingMode.HALF_UP);
        cuMemberRechargeVO.setExchangeRate(exchangeRate);
        cuMemberRechargeVO.setSettlePaymentAmount(settleRechargeAmount);
        return cuMemberRechargeVO;
    }


    @Override
    public CuMemberPetitionVO showMemberPetition(CuMemberRechargeParam memberRechargeParam) {
        CuMemberPetitionListVO cuMemberPetitionListVO;
        CuMemberPetitionVO memberPetitionVo = new CuMemberPetitionVO();
        SysUser loginUser = memberRechargeParam.getSysUser();

        //充值类型
        Integer rechargeMethod = memberRechargeParam.getRechargeMethod();

        R<Map<Integer, ApprovalInfoDTO>> mapR = approvalServiceApi.queryApprovalInfo(Collections.singletonList(EApprovalBusiness.RECHARGE.getValue()), memberRechargeParam.getPkCountry());
        //批次号
        String batchNum = CommonUtil.createSerialNumber(EOrderPrefix.RECHARGE_CODE.getValue());
        memberPetitionVo.setBusinessNo(batchNum);
        //发起部门
        memberPetitionVo.setDeptId(loginUser.getPkDept());
        memberPetitionVo.setDeptStr(loginUser.getDeptName());
        // 发起人
        memberPetitionVo.setSponsorId(loginUser.getUserId());
        memberPetitionVo.setSponsorName(loginUser.getUserName());
        // 日期
        memberPetitionVo.setCreationDate(DateUtils.parseDateTimeToStr(DateUtils.YYYY_MM_DD_HH_MM_SS));

        // 抄送部门
        ApprovalInfoDTO approvalInfoDTO = mapR.getData().get(EApprovalBusiness.RECHARGE.getValue());
        if (null != approvalInfoDTO) {
            memberPetitionVo.setItemList(approvalInfoDTO.getItemList());
            memberPetitionVo.setSendList(approvalInfoDTO.getSendList());
        }

        //查询余额
        List<String> memberCodes = memberRechargeParam.getMemberCode();
        List<CuMemberAccountExt> cuMemberAccountExts = cuMemberAccountService.queryMemberAccountByMemberCodes(null, memberCodes);
        Map<String, CuMemberAccountExt> memberAccounMap = cuMemberAccountExts.stream().collect(Collectors.toMap(CuMemberAccountExt::getMemberCode, o -> o));
        //在线签呈的业务列表展示
        if (CollUtil.isNotEmpty(cuMemberAccountExts)) {
            List<CuMemberPetitionListVO> petitionListVoList = new ArrayList<>();
            //充值账户入参
            List<CuMemberRechargeAccountParam> memberRechargeAccountParams = memberRechargeParam.getMemberRechargeAccountParams();

            for (String memberCode : memberCodes) {
                CuMemberAccountExt cuMemberAccountExt = memberAccounMap.get(memberCode);
                for (CuMemberRechargeAccountParam rechargeAccountParam : memberRechargeAccountParams) {
                    cuMemberPetitionListVO = new CuMemberPetitionListVO();
                    cuMemberPetitionListVO.setMemberCode(memberCode);
                    cuMemberPetitionListVO.setMemberName(cuMemberAccountExt.getMemberName());

                    //充值余额
                    Integer accountType = rechargeAccountParam.getAccountType();
                    BigDecimal balance = ReflectUtils.invokeGetter(cuMemberAccountExt, MemberFieldConstants.ACCOUNT + cuMemberBaseService.getAccountValueById(accountType));
                    //充值金额
                    BigDecimal amount = getRechargeAmount(rechargeAccountParam, rechargeMethod, balance);
                    cuMemberPetitionListVO.setPaymentAmount(amount);
                    cuMemberPetitionListVO.setPkAccount(cuMemberBaseService.translateAccountValue(accountType));
                    cuMemberPetitionListVO.setPaymentBalance(balance);
                    cuMemberPetitionListVO.setRemarks(rechargeAccountParam.getRemarks());
                    cuMemberPetitionListVO.setPaymentAfterBalance(balance.add(amount));
                    petitionListVoList.add(cuMemberPetitionListVO);
                }
            }
            memberPetitionVo.setPetitionListVoList(petitionListVoList);
        }
        return memberPetitionVo;
    }

    /**
     * 获取充值金额
     *
     * @param rechargeMethod       充值方式
     * @param rechargeAccountParam 充值账户入参
     * @return BigDecimal
     */
    private BigDecimal getRechargeAmount(CuMemberRechargeAccountParam rechargeAccountParam, Integer rechargeMethod, BigDecimal balance) {
        BigDecimal rechargeAmount = BigDecimal.ZERO;
        Map<String, BigDecimal> rechargeDetailMap = rechargeAccountParam.getRechargeDetailMap();
        if (MapUtil.isNotEmpty(rechargeDetailMap)) {
            for (Map.Entry<String, BigDecimal> entry : rechargeDetailMap.entrySet()) {
                BigDecimal amount = entry.getValue();
                rechargeAmount = rechargeAmount.add(getAmountByRechargeMethod(rechargeMethod, amount, balance));
            }
        }
        return rechargeAmount;
    }


    @Override
    public Boolean preCharge(String rechargeAmount, Integer pkCountry, Long pkMember, String rechargeCode) {
        BdAccount bdAccount = accountServiceApi.queryAccountByCondition(pkCountry).getData();
        CurrencyDTO currency = cuMemberBaseService.getCurrency(pkCountry);

        CuMemberRecharge memberRecharge = CuMemberRecharge.builder().pkMember(pkMember)
                .rechargeAmount(new BigDecimal(rechargeAmount))
                .pkAccount(bdAccount.getPkId()).pkRate(currency.getPkId())
                .rechargeSource(EOrderPayType.ONLINE.getValue())
                .rechargeCode(rechargeCode).batch(rechargeCode).build();
        memberRecharge.setApproveState(EApproveRechargeStatus.WAIT.getValue());
        memberRecharge.setApproveTime(new Date());
        memberRecharge.setApprover(pkMember);
        memberRecharge.setRemarks("会员端线上充值");
        memberRecharge.setPkCreator(pkMember);
        memberRecharge.setRechargeMethod(ERechargeMethod.ADD.getValue());
        return this.save(memberRecharge);
    }

    @Override
    public CuMemberRecharge getCuMemberRechargeByRechargeCode(String rechargeCode) {
        QueryWrapper<CuMemberRecharge> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("RECHARGE_CODE", rechargeCode);
        return baseMapper.selectOne(queryWrapper);
    }

    @Override
    public List<String> selectMemberRechargeApproveByUserCode(Integer pkCountry, List<String> memberCodes) {
        return baseMapper.selectMemberRechargeApproveByUserCode(pkCountry, memberCodes);
    }

    @Override
    public List<CuMemberRechargeExt> selectOperator(Integer pkCountry) {
        return baseMapper.selectOperator(pkCountry);
    }

    @Override
    public CuMemberRecharge selectRechargeTotalAmount(Date startDate, Date endDate) {
        return baseMapper.selectRechargeTotalAmount(startDate, endDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean redeemPoints(CuMemberRedeemPointsParam param) {
        boolean result = true;
        Long pkMember = param.getPkMember();
        CuMember member = cuMemberService.queryMember(pkMember);
        //汇率
        CurrencyDTO currency = cuMemberBaseService.getCurrency(param.getPkCountry());
        param.setPkRate(currency.getPkId());

        //订单ID
        Long pkOrder = saOrderServiceApi.querySeqList(1).getData().get(0);
        //插入订单信息
        SaOrder saOrder = getSaOrder(param, member, pkOrder);
        String orderCode = saOrder.getOrderCode();

        //插入交易流水、更新余额
        List<CuMemberTrade> memberTradeList = new ArrayList<>();
        CuMemberTrade cuMemberTrade = null;
        CuMemberAccount cuMemberAccount = null;
        Map<Integer, CuMemberAccountRedeemParam> accountParamMap = param.getAccountParamMap();
        if (MapUtil.isNotEmpty(accountParamMap)) {
            cuMemberAccount = CuMemberAccount.builder().pkMember(pkMember).build();
            cuMemberAccount.setPkCountry(param.getPkCountry());
            for (Map.Entry<Integer, CuMemberAccountRedeemParam> entry : accountParamMap.entrySet()) {
                CuMemberAccountRedeemParam value = entry.getValue();
                Integer fieldValue = value.getFieldValue();
                cuMemberTrade = getCuMemberTradeByIntegral(param, value, orderCode);
                ReflectUtils.invokeSetter(cuMemberAccount, MemberFieldConstants.ACCOUNT + fieldValue, value.getAmount().negate());
                if (fieldValue.equals(EAccount.BONUS.getValue())) {
                    BigDecimal withdrawAccount = ReflectUtils.invokeGetter(param.getCuMemberAccountExt(), MemberFieldConstants.WITHDRAW_ACCOUNT + fieldValue);
                    if (withdrawAccount.compareTo(BigDecimal.ZERO) > 0) {
                        ReflectUtils.invokeSetter(cuMemberAccount, MemberFieldConstants.WITHDRAW_ACCOUNT + fieldValue, value.getAmount().negate());
                    }
                }
                memberTradeList.add(cuMemberTrade);
            }
            //增加复消积分流水
            BdAccount bdAccount = accountServiceApi.getBdAccountByFieldValue(param.getPkCountry(), EAccount.RECOIL_INTEGRAL.getValue()).getData();
            CuMemberAccountRedeemParam accountRedeemParam = CuMemberAccountRedeemParam.builder().amount(param.getRedeemAmount().negate())
                    .fieldValue(bdAccount.getFieldValue())
                    .pkAccount(bdAccount.getPkId())
                    .build();

            CuMemberTrade reMemberTrade = getCuMemberTradeByIntegral(param, accountRedeemParam, orderCode);
            memberTradeList.add(reMemberTrade);
        }
        //批量插入流水表
        cuMemberTradeService.batchSaveTradeByMemberCode(memberTradeList);
        //更新账户余额
        if (null != cuMemberAccount) {
            //充值复消积分账户
            ReflectUtils.invokeSetter(cuMemberAccount, MemberFieldConstants.ACCOUNT + EAccount.RECOIL_INTEGRAL.getValue(), param.getRedeemAmount());
            cuMemberAccountService.updateMemberAccount(cuMemberAccount, pkMember);
        }
        //插入日志表
        cuMemberRedeemPointsService.save(getCuMemberRedeemPoints(param, pkOrder));
        //更新会员信息
        //复消剩余收益次数
        Integer resaleIncomeNumber = member.getResaleIncomeNumber();
        //复消剩余收益奖金
        BigDecimal resaleIncomeBonus = member.getResaleIncomeBonus();
        int bonus = MagicNumberConstants.CAPPED_VALUE * param.getTimes();
        CuMember updateMember = CuMember.builder().pkId(pkMember)
                .resaleIncomeNumber(resaleIncomeNumber + param.getTimes())
                .resaleIncomeBonus(resaleIncomeBonus.add(new BigDecimal(Integer.toString(bonus))))
                .build();
        updateMember.setModifiedTime(new Date());
        updateMember.setPkModified(pkMember);

        cuMemberService.updateById(updateMember);
        R<Long> orderResult = saOrderServiceApi.saveOrderInfo(saOrder, getSaOrderItems(param, member));
        if (!orderResult.isSuccess()) {
            result = false;
            throw new RuntimeException("兑换失败");
        }
        return result;
    }

    /**
     * 根据配置扣减兑换币种
     *
     * @param cuMemberAccount     会员账户
     * @param redeemAmount        兑换金额
     * @param areaCurrencyDTOList 币种的扣减配置
     * @return
     */
    @Override
    public CuMemberRedeemPointsParam handleReDeemAmount(CuMemberAccountExt cuMemberAccount, BigDecimal redeemAmount, List<AreaCurrencyDTO> areaCurrencyDTOList, CuMemberRedeemPointsParam pointsParam) {
        boolean isPayment = Boolean.FALSE;
        BigDecimal original = redeemAmount;
        Map<Integer, CuMemberAccountRedeemParam> paramMap = new HashMap<>();
        for (AreaCurrencyDTO areaCurrencyDTO : areaCurrencyDTOList) {
            CuMemberAccountRedeemParam param = null;
            // 兑换金额大于零进行扣减
            if (ComputeUtil.compareValue(redeemAmount)) {
                Long pkMember = cuMemberAccount.getPkMember();
                Integer pkAccount = areaCurrencyDTO.getPkAccount();
                Integer fieldValue = areaCurrencyDTO.getFieldValue();
                // 判断比例是否 > 0, 为强制扣除金额 ，强制扣除金额不满足，直接支付失败
                if (!ComputeUtil.compareValue(areaCurrencyDTO.getDeductRatio())) {
                    BigDecimal[] bd = subtract(redeemAmount, cuMemberAccount, areaCurrencyDTO, paramMap);
                    redeemAmount = bd[0];
                    BigDecimal subtractAmount = bd[1];
                    if (subtractAmount.compareTo(BigDecimal.ZERO) > 0) {
                        param = getCuMemberAccountRedeemParam(pkMember, pkAccount, fieldValue, subtractAmount, paramMap);
                    }
                } else {
                    BigDecimal minPayMoney = ComputeUtil.computeDivide(original.multiply(areaCurrencyDTO.getDeductRatio()), BigDecimal.valueOf(100));
                    BigDecimal balance = getAccountByKey(cuMemberAccount, areaCurrencyDTO, paramMap);
                    if (minPayMoney.compareTo(balance) > 0) {
                        break;
                    }
                    redeemAmount = redeemAmount.subtract(minPayMoney);
                    param = getCuMemberAccountRedeemParam(pkMember, pkAccount, fieldValue, minPayMoney, paramMap);
                }
                if (null != param) {
                    paramMap.put(pkAccount, param);
                }
            }
            // 每次扣除后判断是否扣除完毕，扣除完毕结束
            if (redeemAmount.compareTo(BigDecimal.ZERO) == 0) {
                isPayment = Boolean.TRUE;
                break;
            }
        }
        pointsParam.setIsPayment(isPayment);
        pointsParam.setAccountParamMap(paramMap);
        return pointsParam;
    }

    private CuMemberAccountRedeemParam getCuMemberAccountRedeemParam(Long pkMember, Integer pkAccount, Integer fieldValue, BigDecimal amount, Map<Integer, CuMemberAccountRedeemParam> paramMap) {
        // 取原有币种的扣减金额
        CuMemberAccountRedeemParam param = paramMap.get(pkAccount);
        amount = null == param ? amount : amount.add(param.getAmount());
        return CuMemberAccountRedeemParam.builder().pkMember(pkMember)
                .pkAccount(pkAccount)
                .fieldValue(fieldValue)
                .amount(amount)
                .build();
    }


    /**
     * 兑换按币种扣款金额
     *
     * @param payMoney        待支付金额
     * @param cuMemberAccount 会员账户
     */
    private BigDecimal[] subtract(BigDecimal payMoney, CuMemberAccountExt cuMemberAccount, AreaCurrencyDTO areaCurrencyDTO, Map<Integer, CuMemberAccountRedeemParam> paramMap) {
        BigDecimal subtractAmount = BigDecimal.ZERO;
        if (payMoney.compareTo(BigDecimal.ZERO) > 0) {
            //实际扣减金额
            subtractAmount = payMoney;
            BigDecimal accountValue = getAccountByKey(cuMemberAccount, areaCurrencyDTO, paramMap);
            if (!ComputeUtil.compareValue(accountValue)) {
                return new BigDecimal[]{payMoney, BigDecimal.ZERO};
            }
            if (payMoney.compareTo(accountValue) > 0) {
                payMoney = payMoney.subtract(accountValue);
                subtractAmount = accountValue;
            } else {
                payMoney = BigDecimal.ZERO;
            }
        }
        return new BigDecimal[]{payMoney, subtractAmount};
    }


    /**
     * 通过反射获取账户余额
     *
     * @param cuMemberAccount 账户
     * @return 余额
     */
    private BigDecimal getAccountByKey(CuMemberAccountExt cuMemberAccount, AreaCurrencyDTO areaCurrencyDTO, Map<Integer, CuMemberAccountRedeemParam> paramMap) {
        Integer pkAccount = areaCurrencyDTO.getPkAccount();
        Integer fieldValue = areaCurrencyDTO.getFieldValue();
        BigDecimal accountValue = ReflectUtils.invokeGetter(cuMemberAccount, MemberFieldConstants.ACCOUNT + fieldValue);
        BigDecimal lockAccountValue = ReflectUtils.invokeGetter(cuMemberAccount, MemberFieldConstants.LOCK_ACCOUNT + fieldValue);
        lockAccountValue = null == lockAccountValue ? BigDecimal.ZERO : lockAccountValue;
        accountValue = accountValue.add(lockAccountValue.negate());

        //同一币种时，余额减去前次扣减的金额
        CuMemberAccountRedeemParam cuMemberAccountRedeemParam = paramMap.get(pkAccount);
        if (null != cuMemberAccountRedeemParam) {
            BigDecimal amount = cuMemberAccountRedeemParam.getAmount();
            accountValue = accountValue.compareTo(amount) < 0 ? BigDecimal.ZERO : accountValue.subtract(amount);
        }
        return accountValue;
    }

    /**
     * 订单信息
     *
     * @param param  参数
     * @param member 会员信息
     * @return SaOrder
     */
    private SaOrder getSaOrder(CuMemberRedeemPointsParam param, CuMember member, Long pkOrder) {
        Long pkMember = param.getPkMember();
        BdWaresInfoExt bdWaresInfo = param.getBdWaresInfo();
        BigDecimal achieve = bdWaresInfo.getAchieve().multiply(new BigDecimal(param.getTimes().toString()));
        SaOrder saOrder = SaOrder.builder()
                .pkId(pkOrder)
                .orderCode(CommonUtil.createSerialNumber(EOrderPrefix.ORDER_CODE.getValue()))
                .orderType(EOrderType.RESALE_INTEGRAL_ORDER.getValue())
                .pkRate(param.getPkRate()).orderAmount(param.getRedeemAmount())
                .orderAchieve(achieve)
                .payTime(new Date())
                .deliveryWay(EDelivery.SHOP_DELIVERY.getValue())
                .orderStatus(EYesNo.NO.getIntValue()).pkMember(pkMember)
                .pkPlacement(member.getPkPlaceParent())
                .pkReference(member.getPkParent())
                .uploadAchieve(achieve)
                .build();
        saOrder.setPkCreator(pkMember);
        saOrder.setPkCountry(param.getPkCountry());
        return saOrder;
    }

    /**
     * 订单详情
     *
     * @param param  参数
     * @param member 会员信息
     * @return SaOrderItems
     */
    private SaOrderItems getSaOrderItems(CuMemberRedeemPointsParam param, CuMember member) {
        BdWaresInfoExt bdWaresInfo = param.getBdWaresInfo();
        SaOrderItems saOrderItems = SaOrderItems.builder()
                .pkProduct(bdWaresInfo.getPkProduct())
                .pkAwards(bdWaresInfo.getPkWares())
                .price(bdWaresInfo.getPrice())
                .pkRate(param.getPkRate())
                .achievement(bdWaresInfo.getAchieve())
                .quantity(param.getTimes())
                .pkGrade(member.getPkSettleGrade())
                .pkAwards(member.getPkAwards())
                .orderStatus(EOrderStatus.RECEIVED.getValue())
                .pkWaresSpecsSku(bdWaresInfo.getPkId())
                .pkWaresDetail(bdWaresInfo.getPkWaresDetail())
                .waresQuantity(param.getTimes())
                .build();
        saOrderItems.setPkCountry(param.getPkCountry());
        saOrderItems.setPkCreator(param.getPkMember());
        return saOrderItems;
    }

    /**
     * 通过反射获取账户余额
     *
     * @param cuMemberAccount 账户
     * @param key             键
     * @return 余额
     */
    private BigDecimal getAccountByKey(CuMemberAccountExt cuMemberAccount, Integer key) {
        BigDecimal accountValue = ReflectUtils.invokeGetter(cuMemberAccount, MemberFieldConstants.ACCOUNT + key);
        BigDecimal lockAccountValue = ReflectUtils.invokeGetter(cuMemberAccount, MemberFieldConstants.LOCK_ACCOUNT + key);
        lockAccountValue = null == lockAccountValue ? BigDecimal.ZERO : lockAccountValue;
        accountValue = accountValue.add(lockAccountValue.negate());
        return accountValue;
    }


    /**
     * 复消积分兑换交易澈水
     *
     * @param param        参数
     * @param accountParam 账户参数
     * @param orderCode    订单编号
     * @return CuMemberTrade
     */
    private CuMemberTrade getCuMemberTradeByIntegral(CuMemberRedeemPointsParam param, CuMemberAccountRedeemParam accountParam, String orderCode) {
        Long pkMember = param.getPkMember();
        int pkAccount = accountParam.getPkAccount();
        //充值金额
        BigDecimal tradeAmount = accountParam.getAmount();
        //查询余额
        BigDecimal balance = getAccountByKey(param.getCuMemberAccountExt(), accountParam.getFieldValue());
        CuMemberTrade memberTrade = CuMemberTrade.builder().pkMember(pkMember)
                .tradeCode(CommonUtil.createSerialNumber(EOrderPrefix.RECHARGE_CODE.getValue()))
                .pkAccount(pkAccount)
                .tradeType(ETradeType.REDEED_POINTS.getValue())
                .tradeClassify(ETradeClassify.RECHARGE.getValue())
                .pkRate(param.getPkRate())
                .tradeAmount(tradeAmount.negate())
                .tradeBeforeBalance(balance)
                .tradeBalance(balance.add(tradeAmount.negate()))
                .sourceCode(orderCode)
                .tradeStatue(ETradeStatus.TAKE_EFFECT.getValue())
                .remarks(FinanceMsgConstants.REDEMPTION_COMPOSITE_POINTS)
                .build();
        memberTrade.setPkCountry(param.getPkCountry());
        memberTrade.setPkCreator(pkMember);
        return memberTrade;
    }

    /**
     * 会员兑换复消积分
     *
     * @param param   参数
     * @param pkOrder 订单ID
     * @return CuMemberRedeemPoints
     */
    private CuMemberRedeemPoints getCuMemberRedeemPoints(CuMemberRedeemPointsParam param, Long pkOrder) {
        CuMemberRedeemPoints points = CuMemberRedeemPoints.builder()
                .pkMember(param.getPkMember())
                .pkOrder(pkOrder).amount(param.getRedeemAmount())
                .times(param.getTimes())
                .cappedValue(MagicNumberConstants.CAPPED_VALUE)
                .redeemType(ERedeemType.CONTINENT.getValue())
                .build();
        points.setPkCountry(param.getPkCountry());
        points.setPkCreator(param.getPkMember());
        return points;
    }

}
