package com.jeequan.jeepay.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Pair;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.bill.ChannelBillRQ;
import com.jeequan.jeepay.core.model.params.IsvParams;
import com.jeequan.jeepay.core.model.params.IsvsubMchParams;
import com.jeequan.jeepay.core.model.params.NormalMchParams;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.service.mapper.PayInterfaceConfigMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * <p>
 * 支付接口配置参数表 服务实现类
 * </p>
 *
 * @author [mybatis plus generator]
 * @since 2021-04-27
 */
@Slf4j
@Service
public class PayInterfaceConfigService extends ServiceImpl<PayInterfaceConfigMapper, PayInterfaceConfig> {

    @Autowired
    private PayInterfaceDefineService payInterfaceDefineService;

    @Autowired
    private MchInfoService mchInfoService;

    @Autowired
    private MchAppService mchAppService;

    @Autowired
    private IsvInfoService isvInfoService;

    /**
     * @Author: ZhuXiao
     * @Description: 根据 账户类型、账户号、接口类型 获取支付参数配置
     * @Date: 17:20 2021/4/27
     */
    public PayInterfaceConfig getByInfoIdAndIfCode(String infoType, String infoId, String ifCode) {
        return wrapperByInfoIdAndIfCode(infoType, infoId, ifCode).one();
    }

    public LambdaQueryChainWrapper<PayInterfaceConfig> wrapperByInfoIdAndIfCode(String infoType, String infoId, String ifCode) {
        return lambdaQuery()
                .eq(PayInterfaceConfig::getInfoType, infoType)
                .eq(PayInterfaceConfig::getInfoId, infoId)
                .eq(PayInterfaceConfig::getIfCode, ifCode);
    }

    /**
     * @Author: ZhuXiao
     * @Description: 根据 账户类型、账户号 获取支付参数配置列表
     * @Date: 14:19 2021/5/7
     */
    public List<PayInterfaceDefine> selectAllPayIfConfigListByIsvNo(String infoType, String infoId) {

        // 支付定义列表
        LambdaQueryWrapper<PayInterfaceDefine> queryWrapper = PayInterfaceDefine.gw();
        queryWrapper.eq(PayInterfaceDefine::getState, CS.YES);
        queryWrapper.eq(PayInterfaceDefine::getIsIsvMode, CS.YES); // 支持服务商模式

        List<PayInterfaceDefine> defineList = payInterfaceDefineService.list(queryWrapper);

        // 支付参数列表
        LambdaQueryWrapper<PayInterfaceConfig> wrapper = PayInterfaceConfig.gw();
        wrapper.eq(PayInterfaceConfig::getInfoId, infoId);
        wrapper.eq(PayInterfaceConfig::getInfoType, infoType);
        List<PayInterfaceConfig> configList = this.list(wrapper);

        for (PayInterfaceDefine define : defineList) {
            for (PayInterfaceConfig config : configList) {
                if (define.getIfCode().equals(config.getIfCode())) {
                    define.addExt("ifConfigState", config.getState()); // 配置状态
                }
            }
        }
        return defineList;
    }


    public List<PayInterfaceDefine> selectAllPayIfConfigListByAppId(String appId) {

        MchAppEntity mchAppEntity = mchAppService.getById(appId);

        if (mchAppEntity == null || mchAppEntity.getState() != CS.YES) {
            throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }

        Set<String> isvNoList = isvInfoService.getMchIsvList(mchAppEntity.getMchNo());

        MchInfo mchInfo = mchInfoService.getById(mchAppEntity.getMchNo());

        if (mchInfo == null || mchInfo.getState() != CS.YES) {
            throw new BizException(ApiCodeEnum.SYS_OPERATION_FAIL_SEARCH);
        }
        // 支付定义列表
        LambdaQueryWrapper<PayInterfaceDefine> queryWrapper = PayInterfaceDefine.gw();
        queryWrapper.eq(PayInterfaceDefine::getState, CS.YES);

        Map<String, PayInterfaceConfig> isvPayConfigMap = new HashMap<>(); // 服务商支付参数配置集合

        // 根据商户类型，添加接口是否支持该商户类型条件
        if (mchInfo.getType() == CS.MCH_TYPE_NORMAL) {
            queryWrapper.eq(PayInterfaceDefine::getIsMchMode, CS.YES); // 支持普通商户模式
        }
        if (mchInfo.getType() == CS.MCH_TYPE_ISVSUB) {
            queryWrapper.eq(PayInterfaceDefine::getIsIsvMode, CS.YES); // 支持服务商模式
            // 商户类型为特约商户，服务商应已经配置支付参数
            List<PayInterfaceConfig> isvConfigList = this.list(PayInterfaceConfig.gw()
                    .in(PayInterfaceConfig::getInfoId, isvNoList)
                    .eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.ISV)
                    .eq(PayInterfaceConfig::getState, CS.YES)
                    .ne(PayInterfaceConfig::getIfParams, "")
                    .isNotNull(PayInterfaceConfig::getIfParams));

            for (PayInterfaceConfig config : isvConfigList) {
                config.addExt("mchType", mchInfo.getType());
                isvPayConfigMap.put(config.getIfCode(), config);
            }
        }

        List<PayInterfaceDefine> defineList = payInterfaceDefineService.list(queryWrapper);

        // 支付参数列表
        LambdaQueryWrapper<PayInterfaceConfig> wrapper = PayInterfaceConfig.gw();
        wrapper.eq(PayInterfaceConfig::getInfoId, appId);
        wrapper.eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.MCH_APP);
        List<PayInterfaceConfig> configList = this.list(wrapper);

        for (PayInterfaceDefine define : defineList) {
            define.addExt("mchType", mchInfo.getType()); // 所属商户类型

            for (PayInterfaceConfig config : configList) {
                if (define.getIfCode().equals(config.getIfCode())) {
                    define.addExt("ifConfigState", config.getState()); // 配置状态
                }
            }

            if (mchInfo.getType() == CS.MCH_TYPE_ISVSUB && isvPayConfigMap.get(define.getIfCode()) == null) {
                define.addExt("subMchIsvConfig", CS.NO); // 特约商户，服务商支付参数的配置状态，0表示未配置
            }
        }
        return defineList;
    }


    /**
     * 查询商户app使用已正确配置了通道信息
     */
    public boolean mchAppHasAvailableIfCode(String appId, String ifCode) {

        return this.count(
                PayInterfaceConfig.gw()
                        .eq(PayInterfaceConfig::getIfCode, ifCode)
                        .eq(PayInterfaceConfig::getState, CS.PUB_USABLE)
                        .eq(PayInterfaceConfig::getInfoId, appId)
                        .eq(PayInterfaceConfig::getInfoType, CS.SYS_ROLE_TYPE.MCH_APP)
        ) > 0;

    }


    /**
     * 查询  根据配置的渠道服务商ID去重后的  所有服务商支付参数
     */
    public List<ChannelBillRQ> selectDistinctIsvIfParams(String ifCode) {

        List<ChannelBillRQ> channelBillRQList = new ArrayList<>(); // 去重的配置集合

        List<PayInterfaceConfig> configList = selectIfParamsByIfCodeAndInfoType(ifCode, CS.SYS_ROLE_TYPE.ISV, null);
        if (CollUtil.isEmpty(configList)) {
            log.info("查询支付接口ifCode={}，服务商参数配置为空={}", ifCode, configList);
            return channelBillRQList;
        }

        // 根据渠道商户号去重的配置集合
        List<PayInterfaceConfig> distinctConfigList = new ArrayList<>();
        // channelMchNo 对应的 全部商户应用appId
        Map<String, List<String>> channelMchNoAndMchAppIdsMap = new ConcurrentHashMap<>();

        // 1、循环服务商配置，筛选开启对账的
        // 2、服务商配置去重，依据为：渠道服务商号
        for (PayInterfaceConfig payInterfaceConfig : configList) {

            IsvParams isvParams = IsvParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = IsvParams.getChannelIsvNo(ifCode, isvParams);

            // 未开启分账配置，跳过
            if ((payInterfaceConfig.getIsCheckBill() != null && payInterfaceConfig.getIsCheckBill() != CS.YES)
                    || StringUtils.isBlank(channelMchNo)) {
                continue;
            }

            List<String> isvNoList = channelMchNoAndMchAppIdsMap.get(channelMchNo);
            if (isvNoList == null) {
                isvNoList = new ArrayList<>();
                distinctConfigList.add(payInterfaceConfig);
            }

            isvNoList.add(payInterfaceConfig.getInfoId());
            channelMchNoAndMchAppIdsMap.put(channelMchNo, isvNoList);
        }

        log.info("查询支付接口ifCode={}，即将执行对账的渠道服务商号与系统服务商号MAP={}", ifCode, channelMchNoAndMchAppIdsMap);

        // 服务商配置的不执行对账的渠道子商户号集合
        Pair<List<String>, Set<String>> pair = selectCheckBillConditionByIsvPayConfig(ifCode, true);

        // 构造对账请求，包含服务商支付参数、服务商配置的过滤渠道子商户号
        for (PayInterfaceConfig payInterfaceConfig : distinctConfigList) {
            IsvParams isvParams = IsvParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = IsvParams.getChannelIsvNo(ifCode, isvParams);

            channelBillRQList.add(ChannelBillRQ.buildIsvChannelBillRQ(channelMchNo, payInterfaceConfig.getInfoId(), isvParams,
                    channelMchNoAndMchAppIdsMap.get(channelMchNo), pair.getValue()));
        }

        return channelBillRQList;
    }

    /**
     * 查询  特约商户  根据配置的渠道子商户ID去重后的  所有商户应用支付参数
     */
    public List<ChannelBillRQ> selectDistinctIsvSubMchIfParams(String ifCode) {

        List<ChannelBillRQ> channelBillRQList = new ArrayList<>(); // 去重的配置集合

        List<PayInterfaceConfig> configList = selectIfParams4IsvsubMch(ifCode);
        if (CollUtil.isEmpty(configList)) {
            return channelBillRQList;
        }

        Pair<List<String>, Set<String>> pair = selectCheckBillConditionByIsvPayConfig(ifCode);
        List<String> checkBillAppIdList = pair.getKey();
        Set<String> ignoreCheckBillMchNos = pair.getValue();
        if (CollUtil.isEmpty(checkBillAppIdList)) {
            return channelBillRQList;
        }

        // 根据渠道商户号去重的配置集合
        List<PayInterfaceConfig> distinctConfigList = new ArrayList<>();
        // channelMchNo 对应的 全部商户应用appId
        Map<String, List<String>> channelMchNoAndMchAppIdsMap = new ConcurrentHashMap<>();

        for (PayInterfaceConfig payInterfaceConfig : configList) {

            // 判断服务商是否开启分账
            if (!checkBillAppIdList.contains(payInterfaceConfig.getInfoId())) {
                continue;
            }

            IsvsubMchParams isvsubMchParams = IsvsubMchParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = IsvsubMchParams.getChannelMchNo(ifCode, isvsubMchParams);
            if (StringUtils.isBlank(channelMchNo)) {
                continue;
            }
            // 子商户号在  不参与对账的子商户列表内，则跳过
            if (CollUtil.isNotEmpty(ignoreCheckBillMchNos) && ignoreCheckBillMchNos.contains(channelMchNo)) {
                continue;
            }

            List<String> mchAppIdList = channelMchNoAndMchAppIdsMap.get(channelMchNo);
            if (mchAppIdList == null) {
                mchAppIdList = new ArrayList<>();
                distinctConfigList.add(payInterfaceConfig);
            }

            mchAppIdList.add(payInterfaceConfig.getInfoId());
            channelMchNoAndMchAppIdsMap.put(channelMchNo, mchAppIdList);
        }


        for (PayInterfaceConfig payInterfaceConfig : distinctConfigList) {
            IsvsubMchParams isvsubMchParams = IsvsubMchParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = IsvsubMchParams.getChannelMchNo(ifCode, isvsubMchParams);

            channelBillRQList.add(ChannelBillRQ.buildSubMchChannelBillRQ(channelMchNo, payInterfaceConfig.getInfoId(),
                    isvsubMchParams, channelMchNoAndMchAppIdsMap.get(channelMchNo)));
        }

        return channelBillRQList;
    }

    /**
     * 查询开启对账的服务商下的全部商户应用APPID
     */
    private Pair<List<String>, Set<String>> selectCheckBillConditionByIsvPayConfig(String ifCode) {
        return selectCheckBillConditionByIsvPayConfig(ifCode, false);
    }

    /**
     * 查询开启对账的服务商下的全部商户应用APPID  和  忽略下载账单的渠道子商户号
     */
    private Pair<List<String>, Set<String>> selectCheckBillConditionByIsvPayConfig(String ifCode, boolean onlyGetIgnoreCheckBillMchNos) {

        // 开启对账的  商户应用AppId集合
        List<String> checkBillAppIdList = new ArrayList<>();
        // 服务商配置的不执行对账的渠道子商户号集合
        Set<String> ignoreCheckBillMchNos = new HashSet<>();

        // 查询可执行分账的条件
        List<PayInterfaceConfig> configList = selectIfParamsByIfCodeAndInfoType(ifCode, CS.SYS_ROLE_TYPE.ISV, null);
        if (CollUtil.isEmpty(configList)) {
            return new Pair<>(checkBillAppIdList, ignoreCheckBillMchNos);
        }

        // 筛选开启对账的服务商号集合
        List<String> checkBillIsvNoList = new ArrayList<>();

        for (PayInterfaceConfig payInterfaceConfig : configList) {
            if (payInterfaceConfig.getIsCheckBill() == null || payInterfaceConfig.getIsCheckBill() == CS.NO) {
                continue;
            }

            if (!onlyGetIgnoreCheckBillMchNos) {
                checkBillIsvNoList.add(payInterfaceConfig.getInfoId());
            }

            if (StringUtils.isNotBlank(payInterfaceConfig.getIgnoreCheckBillMchNos())) {
                Collections.addAll(ignoreCheckBillMchNos, payInterfaceConfig.getIgnoreCheckBillMchNos().split(","));
            }
        }

        // 仅查询忽略下载账单的渠道子商户号
        if (onlyGetIgnoreCheckBillMchNos) {
            return new Pair<>(checkBillAppIdList, ignoreCheckBillMchNos);
        }

        // 筛选开启对账的  商户号集合
        Set<String> checkBillMchNoList = isvInfoService.connLambdaQuery()
                .in(IsvUserConnEntity::getIsvNo, checkBillIsvNoList)
                .eq(IsvUserConnEntity::getInfoType, CS.SYS_ROLE_TYPE.MCH)
                .list()
                .stream()
                .map(IsvUserConnEntity::getInfoId)
                .collect(Collectors.toSet());

        if (CollUtil.isEmpty(checkBillMchNoList)) {
            return new Pair<>(checkBillAppIdList, ignoreCheckBillMchNos);
        }

        checkBillAppIdList = mchAppService.lambdaQuery().in(MchAppEntity::getMchNo, checkBillMchNoList)
                .list().stream().map(MchAppEntity::getAppId).collect(Collectors.toList());

        return new Pair<>(checkBillAppIdList, ignoreCheckBillMchNos);
    }

    /**
     * 查询  普通商户  根据配置的渠道子商户ID去重后的  所有商户应用支付参数
     */
    public List<ChannelBillRQ> selectDistinctNormalMchIfParams(String ifCode) {

        List<ChannelBillRQ> channelBillRQList = new ArrayList<>(); // 去重的配置集合

        List<PayInterfaceConfig> configList = selectIfParams4NormalMch(ifCode);
        if (CollUtil.isEmpty(configList)) {
            return channelBillRQList;
        }

        // 根据渠道商户号去重的配置集合
        List<PayInterfaceConfig> distinctConfigList = new ArrayList<>();
        // channelMchNo 对应的 全部商户应用appId
        Map<String, List<String>> channelMchNoAndMchAppIdsMap = new ConcurrentHashMap<>();

        for (PayInterfaceConfig payInterfaceConfig : configList) {

            // 未开启分账配置，跳过
            if (payInterfaceConfig.getIsCheckBill() != null && payInterfaceConfig.getIsCheckBill() != CS.YES) {
                continue;
            }

            NormalMchParams normalMchParams = NormalMchParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = NormalMchParams.getChannelMchNo(ifCode, normalMchParams);
            if (StringUtils.isBlank(channelMchNo)) {
                continue;
            }

            List<String> mchAppIdList = channelMchNoAndMchAppIdsMap.get(channelMchNo);
            if (mchAppIdList == null) {
                mchAppIdList = new ArrayList<>();
                distinctConfigList.add(payInterfaceConfig);
            }

            mchAppIdList.add(payInterfaceConfig.getInfoId());
            channelMchNoAndMchAppIdsMap.put(channelMchNo, mchAppIdList);
        }

        for (PayInterfaceConfig payInterfaceConfig : distinctConfigList) {
            NormalMchParams normalMchParams = NormalMchParams.factory(ifCode, payInterfaceConfig.getIfParams());
            String channelMchNo = NormalMchParams.getChannelMchNo(ifCode, normalMchParams);

            channelBillRQList.add(ChannelBillRQ.buildNormalMchChannelBillRQ(channelMchNo, payInterfaceConfig.getInfoId(),
                    normalMchParams, channelMchNoAndMchAppIdsMap.get(channelMchNo)));
        }

        return channelBillRQList;
    }


    /**
     * 查询  特约商户  支付参数配置
     */
    private List<PayInterfaceConfig> selectIfParams4IsvsubMch(String ifCode) {
        List<MchAppEntity> mchAppEntityList = mchAppService.selectAllIsvsubMchApp();
        if (CollUtil.isEmpty(mchAppEntityList)) {
            return null;
        }

        List<String> appIdList = mchAppEntityList.stream().map(MchAppEntity::getAppId).collect(Collectors.toList());

        return selectIfParamsByIfCodeAndInfoType(ifCode, CS.SYS_ROLE_TYPE.MCH_APP, appIdList);
    }

    /**
     * 查询  普通商户  支付参数配置
     */
    private List<PayInterfaceConfig> selectIfParams4NormalMch(String ifCode) {
        List<MchAppEntity> mchAppEntityList = mchAppService.selectAllNormalMchApp();
        if (CollUtil.isEmpty(mchAppEntityList)) {
            return null;
        }

        List<String> appIdList = mchAppEntityList.stream().map(MchAppEntity::getAppId).collect(Collectors.toList());

        return selectIfParamsByIfCodeAndInfoType(ifCode, CS.SYS_ROLE_TYPE.MCH_APP, appIdList);
    }

    /**
     * 查询 支付参数非空的配置
     */
    private List<PayInterfaceConfig> selectIfParamsByIfCodeAndInfoType(String ifCode, String infoType, List<String> infoIdList) {
        return baseMapper.selectList(PayInterfaceConfig.gw()
                .select(PayInterfaceConfig::getIfCode, PayInterfaceConfig::getInfoId, PayInterfaceConfig::getIfParams, PayInterfaceConfig::getIsCheckBill, PayInterfaceConfig::getIgnoreCheckBillMchNos)
                .eq(PayInterfaceConfig::getIfCode, ifCode)
                .eq(PayInterfaceConfig::getInfoType, infoType)
                .in(CollUtil.isNotEmpty(infoIdList), PayInterfaceConfig::getInfoId, infoIdList)
                .isNotNull(PayInterfaceConfig::getIfParams)
        );
    }


    /**
     * 查询商户支付方式配置的 oauth2条目
     **/
    public String queryIsvSelectOauth2InfoId(String isvNo, String ifCode) {

        PayInterfaceConfig config = this.getByInfoIdAndIfCode(CS.SYS_ROLE_TYPE.ISV, isvNo, ifCode);
        return config == null ? "" : config.getOauth2InfoId();

    }

    /**
     * 查询服务商支付方式配置的 oauth2条目
     **/
    public String queryAgentSelectOauth2InfoId(String agentNo, String ifCode) {
        PayInterfaceConfig config = this.getByInfoIdAndIfCode(CS.SYS_ROLE_TYPE.AGENT, agentNo, ifCode);
        return config == null ? "" : config.getOauth2InfoId();
    }


}
