/*
 *
 */
package com.jeequan.jeepay.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.applyment.ApplymentBasicInfo;
import com.jeequan.jeepay.core.model.applyment.PaywayFee;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.mapper.AgentInfoMapper;
import com.jeequan.jeepay.service.mapper.RateConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 费率配置表 服务实现类
 * </p>
 *
 * @author [mybatis plus generator]
 * @since 2022-03-12
 */
@Service
@Slf4j
public class RateConfigService extends ServiceImpl<RateConfigMapper, RateConfig> {

    @Autowired
    private AgentInfoService agentInfoService;
    @Autowired
    private MchInfoService mchInfoService;
    @Autowired
    private MchAppService mchAppService;
    @Autowired
    private MchPayPassageService mchPayPassageService;
    @Autowired
    private IsvInfoService isvInfoService;

    @Resource
    private AgentInfoMapper agentInfoMapper;




    /**
     * 重新设置费率
     */
    @Transactional(rollbackFor = Exception.class)
    public void resetRate(List<RateConfig> list, LambdaQueryWrapper<RateConfig> delWrapper, RateConfig mainRateConfig, boolean noCheckRuleFlag, List<RateConfig> delList) {

        // 先删除
        remove(delWrapper);

        //插入
        saveBatch(list);

        // 校验: 如果有异常， 那么直接回滚！

        // 下级服务商默认费率 infoId = ISVNO_AGENTDEF ::: 先校验默认费率的设置值
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.AGENTDEF))) {
            checkByDefaultFee(mainRateConfig.getInfoId(), mainRateConfig.getInfoType(), mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.AGENTDEF), PaywayFee.class), "服务商默认费率异常： ", mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 校验下级服务商最大费率, 配置渠道时，不做校验
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.AGENTMAX)) && !CS.SYS_ROLE_TYPE.ISV.equals(mainRateConfig.getInfoType())) {
            checkFeeByMaxFee(mainRateConfig.getInfoId(), mainRateConfig.getInfoType(), CS.SYS_ROLE_TYPE.ISV, mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.AGENTMAX), PaywayFee.class), "服务商费率上限异常： ", mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 商户默认费率 infoId = ISVNO_MCHAPPLYDEF ::: 先校验默认费率的设置值
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.MCHAPPLYDEF))) {
            checkByDefaultFee(mainRateConfig.getInfoId(), mainRateConfig.getInfoType(), mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.MCHAPPLYDEF), PaywayFee.class), "商户默认费率异常： ", mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 商户最大费率 infoId = ISVNO_MCHAPPLYMAX, 配置渠道时，不做校验
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.MCHAPPLYMAX)) && !CS.SYS_ROLE_TYPE.ISV.equals(mainRateConfig.getInfoType())) {
            checkFeeByMaxFee(mainRateConfig.getInfoId(), mainRateConfig.getInfoType(), CS.SYS_ROLE_TYPE.AGENT, mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.MCHAPPLYMAX), PaywayFee.class), "商户费率上限异常： ", mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 校验服务商费率
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.AGENTRATE))) {
            checkFeeByAgent(mainRateConfig.getInfoId(), mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.AGENTRATE), PaywayFee.class), mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 校验商户费率
        if (StringUtils.isNotEmpty(mainRateConfig.extv().getString(RateConfig.MCHRATE))
                && (!noCheckRuleFlag)) { //当不校验规则， 那么直接放行
            checkFeeByMch(mainRateConfig.getInfoId(), null, mainRateConfig.getIfCode(), JSON.parseArray(mainRateConfig.extv().getString(RateConfig.MCHRATE), PaywayFee.class), mainRateConfig.getRange(), mainRateConfig.getMccCode(), mainRateConfig.getIsvNo());
        }

        // 删除集合
        if (delList != null && !delList.isEmpty()) {

            long delCount = 0L;

            long closeMchPassageCount = 0L;

            for (RateConfig rateConfig : delList) {
                boolean isDel = this.remove(Wrappers.lambdaQuery(RateConfig.class)
                        .eq(RateConfig::getInfoId, rateConfig.getInfoId())
                        .eq(RateConfig::getInfoType, rateConfig.getInfoType())
                        .eq(RateConfig::getIfCode, rateConfig.getIfCode())
                        .eq(RateConfig::getWayCode, rateConfig.getWayCode())
                );

                // 关闭商户费率，  同时关闭 商户开启的支付接口
                if (CS.SYS_ROLE_TYPE.MCH_APP.equals(rateConfig.getInfoType())) {
                    boolean isCloseFlag = mchPayPassageService.lambdaUpdate().set(MchPayPassage::getState, CS.NO)
                            .eq(MchPayPassage::getAppId, RateConfig.getRealInfoId(rateConfig.getInfoId()))
                            .eq(MchPayPassage::getIfCode, rateConfig.getIfCode())
                            .eq(MchPayPassage::getWayCode, rateConfig.getWayCode())
                            .eq(MchPayPassage::getState, CS.YES).update();

                    if (isCloseFlag) {
                        closeMchPassageCount++;
                    }
                }

                if (isDel) {
                    delCount++;
                }
            }

            log.info("删除下级配置信息： 共匹配到待删除数据{}条， 删除成功： {}条,  关闭商户通道：{} 个 ", delList.size(), delCount, closeMchPassageCount);
        }
    }

    public Map<String, PaywayFee> queryPaywayFeeMap(String infoId, String infoType, String ifCode, Integer range, String mccCode, String isvNo) {
        return queryPaywayFeeMap(infoId, infoType, ifCode, range, mccCode, isvNo, false);
    }

    public Map<String, PaywayFee> queryPaywayFeeMap(String infoId, String infoType, String ifCode, Integer range, String mccCode, String isvNo, boolean transfer) {

        Map<String, PaywayFee> result = new HashMap<>();

        if (!transfer) {
            Assert.notNull(range, "适用范围[range]不能为空");

            if (!Objects.equals(range, 0) && mccCode == null) {
                return result;
            }
        }

        lambdaQuery().eq(RateConfig::getInfoId, infoId)
                .eq(RateConfig::getInfoType, infoType)
                .eq(RateConfig::getIfCode, ifCode)
                .eq(!ObjectUtils.isEmpty(range), RateConfig::getRange, range)
                .eq(RateConfig::getIsvNo, isvNo)
                .eq(!ObjectUtils.isEmpty(mccCode), RateConfig::getMccCode, mccCode)
                .list().forEach(r -> result.put(r.getWayCode(), r.getPaywayFeeDetail().toJavaObject(PaywayFee.class)));

        return result;
    }

    public Map<String, RateConfig> queryRateConfigMap(String infoId, String infoType, String ifCode, int range, String mccCode, String isvNo) {
        return queryRateConfigMap(Collections.singletonList(infoId), infoType, ifCode, range, mccCode, isvNo);
    }

    public Map<String, RateConfig> queryRateConfigMap(List<String> infoIdList, String infoType, String ifCode, int range, String mccCode, String isvNo) {

        Map<String, RateConfig> result = new HashMap<>();
        if (infoIdList.isEmpty()) {
            return result;
        }

        lambdaQuery().in(RateConfig::getInfoId, infoIdList)
                .eq(RateConfig::getRange, range)
                .eq(RateConfig::getIsvNo, isvNo)
                .eq(!ObjectUtils.isEmpty(mccCode), RateConfig::getMccCode, mccCode)
                .eq(RateConfig::getInfoType, infoType).eq(RateConfig::getIfCode, ifCode)
                .list().forEach(r -> result.put(r.getWayCode(), r));

        return result;
    }


    /**
     * 根据进件信息查询 配置过的费率
     */
//    public Map<String, PaywayFee> queryPaywayFeeMapByApplyment(String applyId) {
//
//        Map<String, PaywayFee> result = new HashMap<>();
//
//        // 没有进件信息
//        if (StringUtils.isEmpty(applyId)) {
//            return result;
//        }
//
//        MchApplyment mchApplyment = mchApplymentService.getById(applyId);
//
//
//        if (mchApplyment == null) {
//            return result;
//        }
//
//        String applyDetailInfo = mchApplyment.getApplyDetailInfo();
//
//        if (StringUtils.isEmpty(applyDetailInfo)) {
//            return result;
//        }
//
//        ApplymentBasicInfo applymentBasicInfo = JSON.parseObject(applyDetailInfo, ApplymentBasicInfo.class);
//
//        if (applymentBasicInfo == null || applymentBasicInfo.getPaywayFeeList() == null) {
//            return result;
//        }
//
//        applymentBasicInfo.getPaywayFeeList().forEach(r -> {
//            r.setState(CS.YES);
//            result.put(r.getWayCode(), r);
//        });
//        return result;
//    }


    /**
     * 查询所有支持的支付方式
     **/
    public Set<String> getAllWayCodes(Collection wayCodes, String ifCode, String infoId, String infoType) {

        Set<String> result = new HashSet<>();

        list(RateConfig.gw().select(RateConfig::getWayCode)
                .in(RateConfig::getWayCode, wayCodes)
                .eq(RateConfig::getInfoId, infoId)
                .eq(RateConfig::getInfoType, infoType)
                .eq(RateConfig::getIfCode, ifCode)
        ).forEach(r -> result.add(r.getWayCode()));

        return result;

    }

    /**
     * 查询所有支持的支付方式
     **/
    public Set<String> getAllWayCodesByInfoId(String ifCode, String infoId, String infoType, String isvNo) {
        return getAllWayCodesByInfoIds(ifCode, Collections.singletonList(infoId), infoType, isvNo);
    }


    /**
     * 查询所有支持的支付方式
     **/
    public Set<String> getAllWayCodesByInfoIds(String ifCode, List<String> infoIds, String infoType, String isvNo) {

        Set<String> result = new HashSet<>();

        if (infoIds.isEmpty()) {
            return result;
        }

        list(RateConfig.gw().select(RateConfig::getWayCode)
                .in(RateConfig::getInfoId, infoIds)
                .eq(RateConfig::getInfoType, infoType)
                .eq(RateConfig::getIfCode, ifCode)
                .eq(RateConfig::getIsvNo, isvNo)
        ).forEach(r -> result.add(r.getWayCode()));

        return result;

    }

    /**
     * 获取费率信息
     **/
    public PaywayFee getPaywayFee(String ifCode, String wayCode, String infoId, String infoType, Integer range, String mercCode,String isvNo) {
        LambdaQueryWrapper<RateConfig> queryWrapper = RateConfig.gw().eq(RateConfig::getIsvNo, isvNo).eq(RateConfig::getInfoId, infoId).eq(RateConfig::getInfoType, infoType).eq(RateConfig::getIfCode, ifCode)
                .eq(RateConfig::getRange, range);
        if (StringUtils.isNotEmpty(mercCode)) {
            queryWrapper.eq(RateConfig::getMccCode, mercCode);
        }
        //如果是线上就写死SCAN
        if(!CS.SETTLEMENT_TYPE.D0.equals(wayCode) && !CS.SETTLEMENT_TYPE.D1.equals(wayCode) && range == 1){
            wayCode = CS.PAY_WAY_CODE.SCAN;
        }
        queryWrapper.eq(RateConfig::getWayCode, wayCode);
        RateConfig rateConfig = getOne(queryWrapper);

        if (rateConfig == null) {
            return null;
        }

        return JSON.toJavaObject(rateConfig.getPaywayFeeDetail(), PaywayFee.class);
    }


    /**
     * 获取费率信息 (商户的配置信息)
     **/
    public PaywayFee getPaywayFeeByMch(String ifCode, String wayCode, String mchApplyId, Integer range, String mccCode,String isvNo) {
        return getPaywayFee(ifCode, wayCode, mchApplyId, CS.SYS_ROLE_TYPE.MCH_APPLYMENT, range, mccCode,isvNo);
    }

    /**
     * 获取费率信息 (渠道商底价)
     **/
    public PaywayFee getPaywayFeeByIsvCost(String ifCode, String wayCode, String isvNo, Integer range, String mccCode) {
        return getPaywayFee(ifCode, wayCode, RateConfig.buildInfoId(isvNo, RateConfig.ISVCOST), CS.SYS_ROLE_TYPE.ISV, range, mccCode,isvNo);
    }

    /**
     * 获取费率信息 (服务商费率)
     **/
    public PaywayFee getPaywayFeeByAgent(String ifCode, String wayCode, String agentNo, Integer range, String mercCode,String isvNo) {
        return getPaywayFee(ifCode, wayCode, RateConfig.buildInfoId(agentNo, RateConfig.AGENTRATE), CS.SYS_ROLE_TYPE.AGENT, range, mercCode,isvNo);
    }


    /**
     * 改造后渠道只会有一个一级代理
     *
     * @since 2023.12.27
     * 校验： 渠道商底价
     **/
    public void checkFeeByIsv(String infoId, String isvNo, String ifCode, int range, String mccCode) {
        Map<String, PaywayFee> mainPaywayFeeMap = lambdaQuery().eq(RateConfig::getInfoId, RateConfig.buildInfoId(isvNo, RateConfig.ISVCOST))
                .eq(RateConfig::getInfoType, CS.SYS_ROLE_TYPE.ISV)
                .eq(RateConfig::getIfCode, ifCode)
                .eq(RateConfig::getRange, range)
                .eq(RateConfig::getIsvNo, isvNo)
                .eq(!ObjectUtils.isEmpty(mccCode), RateConfig::getMccCode, mccCode)
                .list().stream()
                .map(item -> item.getPaywayFeeDetail().toJavaObject(PaywayFee.class))
                .collect(Collectors.toMap(PaywayFee::getWayCode, Function.identity()));

        if (mainPaywayFeeMap.isEmpty()) {
            return;
        }

        // 需要比较的所有集合
        Map<String, List<RateConfig>> allRateConfigMap = new HashMap<>();

        //查询一级服务商，
        List<String> agentInfoIdList = new ArrayList<>();

        // 判断是否支持服务商
        if (SysConfigService.IS_HAS_AGENT_ENT) {
            AgentInfo agentInfo = agentInfoMapper.selectTopAgentInfo(isvNo);
            agentInfoIdList.add(RateConfig.appendInfoByAgent(agentInfo.getAgentNo()));
        }

        Map<String, RateConfig> agentResult = queryRateConfigMap(agentInfoIdList, CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentResult, allRateConfigMap); // 添加到判断列表

        // 查询服务商默认费率
        Map<String, RateConfig> agentDefResult = queryRateConfigMap(Collections.singletonList(infoId + "_" + RateConfig.AGENTDEF), CS.SYS_ROLE_TYPE.ISV, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentDefResult, allRateConfigMap); // 添加到判断列表

        // 查询商户默认费率
        Map<String, RateConfig> mchDefResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByMchDef(infoId)), CS.SYS_ROLE_TYPE.ISV, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(mchDefResult, allRateConfigMap); // 添加到判断列表

        // 查询直属商户费率
        List<String> mchIdList = new ArrayList<>();
        mchInfoService.lambdaQuery().select(MchInfo::getMchNo)
                .like(com.jeequan.jeepay.core.entity.MchInfo::getAgentNoArr, infoId)
                .and(c -> c.eq(MchInfo::getAgentNo, "")
                        .or()
                        .isNull(com.jeequan.jeepay.core.entity.MchInfo::getAgentNo))
                .list().forEach(r -> mchIdList.add(r.getMchNo()));

        List<String> mchAppInfoIdList = new ArrayList<>();
        if (!mchIdList.isEmpty()) {
            mchAppService.lambdaQuery()
                    .select(MchAppEntity::getAppId)
                    .in(MchAppEntity::getMchNo, mchIdList)
                    .list().forEach(r -> mchAppInfoIdList.add(RateConfig.appendInfoByMchApp(r.getAppId())));
        }
        Map<String, RateConfig> mchAppResult = queryRateConfigMap(mchAppInfoIdList, CS.SYS_ROLE_TYPE.MCH_APP, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(mchAppResult, allRateConfigMap); // 添加到判断列表

        // 比较
        commonCompare(mainPaywayFeeMap, allRateConfigMap, -10, "");
    }

    /**
     * 校验： 服务商费率
     **/
    public void checkFeeByAgent(String agentNo, String ifCode, List<PaywayFee> mainPaywayFeeList, int range, String mccCode, String isvNo) {

        if (mainPaywayFeeList.isEmpty()) {
            return;
        }

        // map
        Map<String, PaywayFee> mainPaywayFeeMap = new HashMap<>();
        mainPaywayFeeList.forEach(r -> mainPaywayFeeMap.put(r.getWayCode(), r));

        // 需要比较的所有集合 （ 配置费率 >= 上级费率 ）
        Map<String, List<RateConfig>> allRateConfigMapByGe = new HashMap<>();

        // 查询上级服务商
        AgentInfo currentAgentInfo = agentInfoService.getById(agentNo);
        if (StringUtils.isNotEmpty(currentAgentInfo.getPid())) {
            Map<String, RateConfig> parentAgentResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByAgent(currentAgentInfo.getPid())), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
            addAllRateConfigMap(parentAgentResult, allRateConfigMapByGe); // 添加到判断列表
        }

        // 查询渠道商底价
        Map<String, RateConfig> isvResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByIsvCost(isvNo)), CS.SYS_ROLE_TYPE.ISV, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(isvResult, allRateConfigMapByGe); // 添加到判断列表

        // 判断
        log.info("====校验服务商底价");
        commonCompare(mainPaywayFeeMap, allRateConfigMapByGe, 10, "");


        // 需要比较的所有集合 （ 配置费率 《= 下级的费率 ）
        Map<String, List<RateConfig>> allRateConfigMapByLe = new HashMap<>();

        //查询下级服务商，
        List<String> subAgentInfoIdList = new ArrayList<>();
        agentInfoService.list(AgentInfo.gw().select(AgentInfo::getAgentNo).eq(AgentInfo::getPid, agentNo))
                .forEach(r -> subAgentInfoIdList.add(RateConfig.appendInfoByAgent(r.getAgentNo())));

        Map<String, RateConfig> agentResult = queryRateConfigMap(subAgentInfoIdList, CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentResult, allRateConfigMapByLe); // 添加到判断列表


        // 查询服务商默认费率
        Map<String, RateConfig> agentDefResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByAgentDef(agentNo)), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentDefResult, allRateConfigMapByLe); // 添加到判断列表

        // 查询商户默认费率
        Map<String, RateConfig> mchDefResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByMchDef(agentNo)), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(mchDefResult, allRateConfigMapByLe); // 添加到判断列表

        // 查询下级商户费率
        List<String> mchIdList = new ArrayList<>();
        mchInfoService.list(MchInfo.gw().select(MchInfo::getMchNo).eq(com.jeequan.jeepay.core.entity.MchInfo::getAgentNo, agentNo))
                .forEach(r -> mchIdList.add(r.getMchNo()));

        List<String> mchAppInfoIdList = new ArrayList<>();
        if (!mchIdList.isEmpty()) {
            mchAppService.lambdaQuery().select(MchAppEntity::getAppId).in(MchAppEntity::getMchNo, mchIdList).list().forEach(r -> mchAppInfoIdList.add(RateConfig.appendInfoByMchApp(r.getAppId())));
        }
        Map<String, RateConfig> mchAppResult = queryRateConfigMap(mchAppInfoIdList, CS.SYS_ROLE_TYPE.MCH_APP, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(mchAppResult, allRateConfigMapByLe); // 添加到判断列表

        // 判断
        commonCompare(mainPaywayFeeMap, allRateConfigMapByLe, -10, "");
    }

    /**
     * 校验： 商户费率 可通过 mchNo 或者  mchAppId查询
     **/
    public void checkFeeByMch(String mchNo, String mchAppId, String ifCode, List<PaywayFee> mainPaywayFeeList, int range, String mccCode, String isvNo) {

        if (mainPaywayFeeList.isEmpty()) {
            return;
        }

        // map
        Map<String, PaywayFee> mainPaywayFeeMap = new HashMap<>();
        mainPaywayFeeList.forEach(r -> mainPaywayFeeMap.put(r.getWayCode(), r));

        // 需要比较的所有集合
        Map<String, List<RateConfig>> allRateConfigMap = new HashMap<>();

        // 查询商户信息
        MchInfo mchInfo;
        if (StringUtils.isNotEmpty(mchAppId)) {
            mchInfo = mchInfoService.getById(mchAppService.getById(mchAppId).getMchNo());
        } else {
            mchInfo = mchInfoService.getById(mchNo);
        }

        // 普通商户不校验
        if (mchInfo.getType() == CS.MCH_TYPE_NORMAL) {
            return;
        }

        // 服务商下的商户
        if (StringUtils.isNotEmpty(mchInfo.getAgentNo())) {
            // 代理费率
            Map<String, RateConfig> agentResult = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByAgent(mchInfo.getAgentNo())), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
            addAllRateConfigMap(agentResult, allRateConfigMap); // 添加到判断列表

        } else { // 运营直属商户
            Set<String> isvNoList = isvInfoService.getMchIsvList(mchNo);
            List<String> isvCostList = isvNoList.stream()
                    .map(r -> RateConfig.appendInfoByIsvCost(RateConfig.appendInfoByIsvCost(r)))
                    .collect(Collectors.toList());
            Map<String, RateConfig> agentResult = queryRateConfigMap(isvCostList, CS.SYS_ROLE_TYPE.ISV, ifCode, range, mccCode, isvNo);
            addAllRateConfigMap(agentResult, allRateConfigMap); // 添加到判断列表
        }

        // 比较代理费率
        commonCompare(mainPaywayFeeMap, allRateConfigMap, 10, "");

        if (StrUtil.isNotEmpty(mchInfo.getAgentNo())) {
            // 服务商户费率上限校验
            Map<String, RateConfig> mchMax = queryRateConfigMap(Collections.singletonList(RateConfig.appendInfoByMchMax(mchInfo.getAgentNo())), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
            allRateConfigMap = new HashMap<>();
            addAllRateConfigMap(mchMax, allRateConfigMap); // 添加到判断列表
            // 服务商户费率上限校验
            commonCompare(mainPaywayFeeMap, allRateConfigMap, -10, "");
        }
    }


    /**
     * 校验： 服务商默认费率， 商户默认费率
     **/
    public void checkByDefaultFee(String infoId, String infoType, String ifCode, List<PaywayFee> mainPaywayFeeList, String errorInfoPrefix, int range, String mccCode, String isvNo) {

        if (mainPaywayFeeList.isEmpty()) {
            return;
        }

        // map
        Map<String, PaywayFee> mainPaywayFeeMap = new HashMap<>();
        mainPaywayFeeList.forEach(r -> mainPaywayFeeMap.put(r.getWayCode(), r));


        // 需要比较的所有集合
        Map<String, List<RateConfig>> allRateConfigMap = new HashMap<>();

        // 查询主配置费率
        String configInfoId = CS.SYS_ROLE_TYPE.ISV.equals(infoType) ? RateConfig.appendInfoByIsvCost(infoId) : RateConfig.appendInfoByAgent(infoId);
        Map<String, RateConfig> agentResult = queryRateConfigMap(Collections.singletonList(configInfoId), infoType, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentResult, allRateConfigMap); // 添加到判断列表

        // 比较
        commonCompare(mainPaywayFeeMap, allRateConfigMap, 10, errorInfoPrefix);
    }

    /**
     * 校验： 服务商最大费率， 商户最大费率
     **/
    public void checkFeeByMaxFee(String infoId, String infoType, String compareInfoType,
                                 String ifCode, List<PaywayFee> mainPaywayFeeList, String errorInfoPrefix, int range, String mccCode, String isvNo) {

        if (mainPaywayFeeList.isEmpty()) {
            return;
        }

        // map
        Map<String, PaywayFee> mainPaywayFeeMap = new HashMap<>();
        mainPaywayFeeList.forEach(r -> mainPaywayFeeMap.put(r.getWayCode(), r));

        // 需要比较的所有集合
        Map<String, List<RateConfig>> allRateConfigMap = new HashMap<>();

        // 查询主配置费率
        String configInfoId = CS.SYS_ROLE_TYPE.ISV.equals(compareInfoType) ? RateConfig.appendInfoByAgentDef(infoId) : RateConfig.appendInfoByMchDef(infoId);

        Map<String, RateConfig> agentResult = queryRateConfigMap(Collections.singletonList(configInfoId), infoType, ifCode, range, mccCode, isvNo);
        addAllRateConfigMap(agentResult, allRateConfigMap); // 添加到判断列表

        // 比较
        commonCompare(mainPaywayFeeMap, allRateConfigMap, 10, errorInfoPrefix);

        // 查询上级的进件费率上限, 渠道商没有上级
        if (!CS.SYS_ROLE_TYPE.ISV.equals(compareInfoType)) {

            AgentInfo agentInfo = agentInfoService.getById(infoId);
            allRateConfigMap.clear();

            // 服务商的上级
            if (agentInfo.getPid() != null) {
                // 服务商上级为服务商
                configInfoId = RateConfig.appendInfoByMchMax(agentInfo.getPid());
                Map<String, RateConfig> mchMaxResult = queryRateConfigMap(Collections.singletonList(configInfoId), CS.SYS_ROLE_TYPE.AGENT, ifCode, range, mccCode, isvNo);
                addAllRateConfigMap(mchMaxResult, allRateConfigMap);

                commonCompare(mainPaywayFeeMap, allRateConfigMap, -10, errorInfoPrefix);
            } else {
                // 服务商上级为渠道商
                configInfoId = RateConfig.appendInfoByMchMax(agentInfo.getIsvNo());
                Map<String, RateConfig> mchMaxResult = queryRateConfigMap(Collections.singletonList(configInfoId), CS.SYS_ROLE_TYPE.ISV, ifCode, range, mccCode, isvNo);
                addAllRateConfigMap(mchMaxResult, allRateConfigMap);

                commonCompare(mainPaywayFeeMap, allRateConfigMap, -10, errorInfoPrefix);
            }
        }
    }


    /**
     * 比较配置信息 与 查询到的
     **/
    private void commonCompare(Map<String, PaywayFee> mainPaywayFeeMap, Map<String, List<RateConfig>> allRateConfigMap, int expect, String prefix) {

        // 遍历比较
        mainPaywayFeeMap.forEach((wayCode, mainFee) -> {
            List<RateConfig> compareRateConfigList = allRateConfigMap.get(wayCode);
            if (CollUtil.isEmpty(compareRateConfigList)) {
                return;
            }

            for (RateConfig rateConfig : compareRateConfigList) {
                PaywayFee compareFee = rateConfig.getPaywayFeeDetail().toJavaObject(PaywayFee.class);
                if (compareFee == null) {
                    continue;
                }

                // 有问题直接抛异常。
                try {
                    mainFee.compareByFee(compareFee, expect, "$INFONAME$");
                } catch (Exception e) {
                    log.info("费率校验异常", e);
                    throw new BizException(prefix + e.getMessage().replace("$INFONAME$", getInfoName(rateConfig.getInfoId(), rateConfig.getInfoType())));
                }
            }
        });
    }


    /**
     * 添加配置条目
     **/
    private void addAllRateConfigMap(Map<String, RateConfig> dbResult, Map<String, List<RateConfig>> allRateConfigMap) {
        dbResult.keySet().forEach(wayCode -> {
            allRateConfigMap.computeIfAbsent(wayCode, k -> new ArrayList<>());
            allRateConfigMap.get(wayCode).add(dbResult.get(wayCode));
        });
    }

    /**
     * 解析infoName
     **/
    public String getInfoName(String infoId, String infoType) {

        if (infoId.contains(RateConfig.AGENTRATE) || CS.SYS_ROLE_TYPE.AGENT.equals(infoType)) { // 服务商费率

            return "服务商:" + agentInfoService.getById(RateConfig.getRealInfoId(infoId)).getAgentShortName();

        } else if (infoId.contains(RateConfig.MCHRATE)) {

            return "商户:" + mchAppService.getById(RateConfig.getRealInfoId(infoId)).getMchNo();


        } else if (infoId.contains(RateConfig.ISVCOST)) {

            return "渠道商底价";


        } else if (infoId.contains(RateConfig.AGENTDEF)) {

            return "服务商默认费率";


        } else if (infoId.contains(RateConfig.MCHAPPLYDEF)) {
            return "商户默认费率";
        }

        return "";

    }


    /**
     * 查询服务商支持进件的wayCodes
     */
    public Set<String> queryWayCodesByIsvApplymentSupport(Collection<String> wayCodes, String ifCode, String isvNo) {

        HashSet<String> result = new HashSet<>();

        if (wayCodes.isEmpty()) {
            return result;
        }

        list(RateConfig.gw().select(RateConfig::getWayCode)
                .in(RateConfig::getWayCode, wayCodes)
                .eq(RateConfig::getIfCode, ifCode)
                .eq(RateConfig::getInfoId, RateConfig.appendInfoByIsvCost(isvNo))
                .eq(RateConfig::getInfoType, CS.SYS_ROLE_TYPE.ISV)
                .eq(RateConfig::getApplymentSupport, CS.YES)).forEach(r -> result.add(r.getWayCode()));

        return result;
    }


    /**
     * 查询商户的费率配置 (用作支付下单校验)
     **/
    public PaywayFee queryCreateOrderPaywayFee(String mchApplyId, String isvNo, String mchAppId, String ifCode, String wayCode) {
        MchAppEntity mchApp = mchAppService.getById(mchAppId);
        MchInfo mchInfo = mchInfoService.getById(mchApp.getMchNo());
        if(!CS.SETTLEMENT_TYPE.D0.equals(wayCode) && !CS.SETTLEMENT_TYPE.D1.equals(wayCode) && mchApp.getRange() == 1){
            //TODO 线上费率 写死 SCAN 并且不是D0或者D1
            wayCode = CS.PAY_WAY_CODE.SCAN;
        }
        // 特约商户
        if (mchInfo.getType() == CS.MCH_TYPE_ISVSUB) {

            // 检查服务商
            if (getPaywayFeeByIsvCost(ifCode, wayCode, isvNo, mchApp.getRange(), mchApp.getMccCode()) == null) {
                throw new BizException("费率配置有误[上级渠道商底价未配置]");
            }

            // 检查服务商
            if (StringUtils.isNotEmpty(mchInfo.getAgentNo())) {

                // 所有的服务商infoId
                List<String> agentInfoIdList = new ArrayList<>();
                agentInfoService.queryAllParentAgentNo(mchInfo.getAgentNo()).forEach(r -> agentInfoIdList.add(RateConfig.appendInfoByAgent(r)));

                // 查询服务商的配置项
                LambdaQueryWrapper<RateConfig> queryWrapper = RateConfig.gw().in(RateConfig::getInfoId, agentInfoIdList).eq(RateConfig::getIfCode, ifCode).eq(RateConfig::getWayCode, wayCode).eq(RateConfig::getRange, mchApp.getRange()).eq(RateConfig::getIsvNo, isvNo);
                if (StringUtils.isNotEmpty(mchApp.getMccCode())) {
                    queryWrapper.eq(RateConfig::getMccCode, mchApp.getMccCode());
                }
                long rateCount = this.count(queryWrapper);

                if (agentInfoIdList.size() != rateCount) {
                    throw new BizException("费率配置有误[上级服务商未配置]");
                }
            }
        }

        return getPaywayFeeByMch(ifCode, wayCode, mchApplyId, mchApp.getRange(), mchApp.getMccCode(),isvNo);
    }

    /**
     * 查询转账费率信息， 可能为空
     **/
    public PaywayFee queryTransferCreateOrderPaywayFee(String mchApplyId, String isvNo, String mchAppId, String ifCode, String wayCode) {

        MchAppEntity mchApp = mchAppService.selectById(mchAppId);

        // 查询商户是否有配置
        PaywayFee result = getPaywayFeeByMch(ifCode, wayCode, mchApplyId, mchApp.getRange(), mchApp.getMccCode(),isvNo);

        // 商户未配置， 直接返回null
        if (result == null) {
            return null;
        }

        // 如果有配置， 那么需要判断服务商等一系列是否配置（有异常会向外抛）。
        return queryCreateOrderPaywayFee(mchApplyId, isvNo, mchAppId, ifCode, wayCode);
    }

    /**
     * 保存进件商户的费率信息
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveMchApplymentRate(MchApplyment mRecord) {
        MchAppEntity mchApp = mchAppService.getById(mRecord.getAutoConfigMchAppId());

        ApplymentBasicInfo applymentBasicInfo = JSON.parseObject(mRecord.getApplyDetailInfo(), ApplymentBasicInfo.getImplInfoClass(mRecord.getIfCode()));
        remove(Wrappers.<RateConfig>lambdaQuery()
                .eq(RateConfig::getInfoType, CS.SYS_ROLE_TYPE.MCH_APPLYMENT)
                .eq(RateConfig::getIsvNo, mRecord.getIsvNo())
                .eq(RateConfig::getInfoId, mRecord.getApplyId()));
        // 再保存费率
        List<RateConfig> mchApplymentRateList = new ArrayList<>();

        Assert.notNull(applymentBasicInfo.getPaywayFeeList(), "缺少费率信息");
        for (PaywayFee paywayFee : applymentBasicInfo.getPaywayFeeList()) {
            RateConfig rateConfig = new RateConfig();
            mchApplymentRateList.add(rateConfig);
            rateConfig.setIsvNo(mRecord.getIsvNo());
            rateConfig.setInfoType(CS.SYS_ROLE_TYPE.MCH_APPLYMENT);
            rateConfig.setInfoId(mRecord.getApplyId());
            rateConfig.setIfCode(mRecord.getIfCode());
            if (paywayFee.getFeeType().equals(PaywayFee.FEE_TYPE_SINGLE)) {
                rateConfig.setFeeRate(paywayFee.getFeeRate());
            }

            rateConfig.setRange(mchApp.getRange());
            rateConfig.setMccCode(mchApp.getMccCode());
            rateConfig.setWayCode(paywayFee.getWayCode());
            rateConfig.setFeeType(paywayFee.getFeeType());
            rateConfig.setPaywayFeeDetail((JSONObject) JSON.toJSON(paywayFee));
        }

        saveBatch(mchApplymentRateList);
    }


}
