package cn.ey.biz.module.system.service.rate;

import cn.ey.biz.common.pojo.R;
import cn.ey.biz.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.ey.biz.module.system.api.rate.dto.*;
import cn.ey.biz.module.system.convert.rate.DictFeeSharingFactorConvert;
import cn.ey.biz.module.system.convert.rate.FinanceRateConvert;
import cn.ey.biz.module.system.dal.dataobject.codeblock.CodeBlockDO;
import cn.ey.biz.module.system.dal.dataobject.rate.*;
import cn.ey.biz.module.system.dal.dataobject.user.AdminUserDO;
import cn.ey.biz.module.system.dal.mapper.codeblock.CodeBlockMapper;
import cn.ey.biz.module.system.dal.mapper.rate.*;
import cn.ey.biz.module.system.enums.ErrorCodeConstants;
import cn.ey.biz.module.system.enums.rate.RuleSortEnum;
import cn.ey.biz.module.system.enums.rate.SourceTypeEnum;
import cn.ey.biz.module.system.service.category.dynamic.handler.CategoryRequestHandler;
import cn.ey.biz.module.system.service.rate.dto.DictGradeLevelDTO;
import cn.ey.biz.module.system.service.rate.dto.GdsRateConfigDTO;
import cn.ey.biz.module.system.service.rate.dto.RateConfigDTO;
import cn.ey.biz.module.system.service.rate.dto.RateScopeConfigDTO;
import cn.ey.biz.module.system.service.user.AdminUserService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import static cn.ey.biz.common.exception.util.ServiceExceptionUtil.exception;

/**
 * 财务费率 Service 实现类
 *
 * @author ShiJie-SJ.Li
 * @date 2023/06/13
 */
@Slf4j
@Service
@Validated
public class FinanceRateServiceImpl implements FinanceRateService {

    @Resource
    private AdminUserService adminUserService;

    @Resource
    private DictHrBillRateMapper dictHrBillRateMapper;

    @Resource
    private DictFeeSharingFactorMapper dictFeeSharingFactorMapper;

    @Resource
    private DictCostRateMapper dictCostRateMapper;

    @Resource
    private CategoryRequestHandler categoryRequestHandler;

    @Resource
    private DictLongTermRateMapper dictLongTermRateMapper;

    @Resource
    private DictFirmBillRateMapper dictFirmBillRateMapper;

    @Resource
    private DictGdsRateValueMapper dictGdsRateValueMapper;

    @Resource
    private DictSurchargeRateFirmMapper dictSurchargeRateFirmMapper;

    @Resource
    private CodeBlockMapper codeBlockMapper;

    @Resource
    private DictSurchargeRateGdsExternalMapper dictSurchargeRateGdsExternalMapper;

    @Resource
    private DictSurchargeRateGdsInternalMapper dictSurchargeRateGdsInternalMapper;

    @Resource
    private DictFirmCostRateMapper dictFirmCostRateMapper;

    @Resource
    private DictCostRateSurchargeMapper dictCostRateSurchargeMapper;

    /**
     * 所有
     */
    private final String ALL = "ALL";


    /**
     * 通过GPN获取费率
     *
     * @param rateForGpnReqDTO 率gpn点播dto
     * @return {@link List}<{@link RateResDTO}>
     */
    @Override
    public List<RateResDTO> getRateForGpn(RateForGpnReqDTO rateForGpnReqDTO) {
        // 人员信息
        AtomicReference<AdminUserDO> adminUserDO = new AtomicReference<>(getUserForGpn(rateForGpnReqDTO.getGpn()));
        // 人员codeblock
        CodeBlockDO userCodeBlockDO = getCodeBlock(adminUserDO.get().getCodeBlock());
        // 人员codeblock
        String userCodeBlockCode = userCodeBlockDO.getCodeBlockCode();
        // 人员类型
        String sourceType = userCodeBlockDO.getSourceType();
        // by firm
        if (StrUtil.equals(SourceTypeEnum.FIRM.getValue(), sourceType)) {
            log.info("[财务费率服务]进入FIRM rate查询：{}", sourceType);
            return getFirmRateForGpn(rateForGpnReqDTO, adminUserDO.get());
        }
        // by gds
        if (StrUtil.equals(SourceTypeEnum.GDS.getValue(), sourceType)) {
            if (StrUtil.isEmpty(rateForGpnReqDTO.getEngCodeBlockCode())) {
                throw exception(ErrorCodeConstants.FINANCE_REQUEST_PARAMETER_ERROR);
            }
            RateForGpnReqDTO rate = new RateForGpnReqDTO();
            BeanUtil.copyProperties(rateForGpnReqDTO, rate);
            rate.setCompetencyCode(userCodeBlockDO.getCompetencyCode());
            rate.setMgmtRegion(userCodeBlockDO.getMrCode());
            rate.setCountryRegionCode(userCodeBlockDO.getCountryRegionCode());
            rate.setSlCode(userCodeBlockDO.getSlCode());
            rate.setSslCode(userCodeBlockDO.getSslCode());
            rate.setSmuCode(userCodeBlockDO.getSmuCode());
            rate.setPeakStartDate(rateForGpnReqDTO.getPeakStartDate());
            rate.setPeakEndDate(rateForGpnReqDTO.getPeakEndDate());
            rate.setGpn(rateForGpnReqDTO.getGpn());
            rate.setRankGrade(StrUtil.toString(adminUserDO.get().getGradeId()));
            rate.setEngCodeBlockCode(rateForGpnReqDTO.getEngCodeBlockCode());
            return getGdsRate(userCodeBlockCode, rate);
        }
        return null;
    }

    /**
     * 通过配置获取FIRM费率
     *
     * @param rateForFirmConfReqDTO 率公司配置要求dto
     * @return {@link List}<{@link RateResDTO}>
     */
    @Override
    public List<RateResDTO> getFirmRateForConfig(RateForFirmConfReqDTO rateForFirmConfReqDTO) {
        log.info("[财务费率服务]进入FIRM rate by config查询：{}", JSONObject.toJSONString(rateForFirmConfReqDTO));
        return getFirmRate(FinanceRateConvert.INSTANCE.convert3FirmRateConfigDTO(rateForFirmConfReqDTO));
    }

    /**
     * 通过配置获取费GDS率
     *
     * @param rateForGdsConfReqDTO 率为gds conf点播dto
     * @return {@link List}<{@link RateResDTO}>
     */
    @Override
    public List<RateResDTO> getGdsRateForConfig(RateForGdsConfReqDTO rateForGdsConfReqDTO) {
        CodeBlockDO codeBlock = getCodeBlock(rateForGdsConfReqDTO.getEngCodeBlockCode());
        RateForGpnReqDTO rateForGpnReqDTO = new RateForGpnReqDTO();
        rateForGpnReqDTO.setCompetencyCode(codeBlock.getCompetencyCode());
        rateForGpnReqDTO.setMgmtRegion(codeBlock.getMrCode());
        rateForGpnReqDTO.setCountryRegionCode(codeBlock.getCountryRegionCode());
        rateForGpnReqDTO.setSlCode(codeBlock.getSlCode());
        rateForGpnReqDTO.setSslCode(codeBlock.getSslCode());
        rateForGpnReqDTO.setFiscalYear(rateForGdsConfReqDTO.getFiscalYear());
        rateForGpnReqDTO.setSmuCode(codeBlock.getSmuCode());
        rateForGpnReqDTO.setRankGrade(rateForGdsConfReqDTO.getRankGrade());
        rateForGpnReqDTO.setEngCodeBlockCode(rateForGdsConfReqDTO.getEngCodeBlockCode());
        rateForGpnReqDTO.setPeakStartDate(rateForGdsConfReqDTO.getPeakStartDate());
        rateForGpnReqDTO.setPeakEndDate(rateForGdsConfReqDTO.getPeakEndDate());
        return getGdsRate(rateForGdsConfReqDTO.getEmpCodeBlockCode(), rateForGpnReqDTO);
    }

    /**
     * 获取费用分摊系数
     *
     * @param feeSharingFactorReqDTO 费用分摊系数参数
     * @return {@link FeeSharingFactorReqDTO}
     */
    @Override
    public FeeSharingFactorResDTO getFeeSharingFactor(FeeSharingFactorReqDTO feeSharingFactorReqDTO) {
        log.info("[财务费率服务]获取费用分摊系数入参：{}", JSONObject.toJSONString(feeSharingFactorReqDTO));
        DictFeeSharingFactorDO feeSharingFactorDO = dictFeeSharingFactorMapper
                .selectOne(new LambdaQueryWrapperX<DictFeeSharingFactorDO>().eq(DictFeeSharingFactorDO::getStatus, 0)
                        .eq(DictFeeSharingFactorDO::getFiscalYear, feeSharingFactorReqDTO.getFiscalYear())
                        .eq(DictFeeSharingFactorDO::getCountryRegion, feeSharingFactorReqDTO.getCountryRegion())
                        .eq(DictFeeSharingFactorDO::getSlCode, feeSharingFactorReqDTO.getSlCode())
                        .eq(DictFeeSharingFactorDO::getSslCode, feeSharingFactorReqDTO.getSslCode()));
        log.info("[财务费率服务]查询费用分摊系数结果：{}", JSONObject.toJSONString(feeSharingFactorDO));
        return DictFeeSharingFactorConvert.INSTANCE.convertFeeSharingFactorResDTO(feeSharingFactorDO);
    }


    /**
     * 获取人员费率以及Surcharges（日期范围）
     *
     * @param rateReqDTO 率要求dto
     * @return {@link List}<{@link RateResDTO}>
     */
    @Override
    public List<RateResDTO> getRateForDateScope(RateReqDTO rateReqDTO) {
        log.info("[财务费率服务]获取HR系统费率入参：{}", JSONObject.toJSONString(rateReqDTO));
        if (!validConfig(RateScopeConfigDTO.class, FinanceRateConvert.INSTANCE.convertRateScopeConfigDTO(rateReqDTO))) {
            throw exception(ErrorCodeConstants.FINANCE_REQUEST_PARAMETER_ERROR);
        }
        RateForGpnReqDTO rateForGpnReqDTO = new RateForGpnReqDTO();
        rateForGpnReqDTO.setGpn(rateReqDTO.getGpn());
        rateForGpnReqDTO.setEngCodeBlockCode(rateReqDTO.getCodeBlockCode());
        rateForGpnReqDTO.setFiscalYear(rateReqDTO.getFiscalYear());
        rateForGpnReqDTO.setPeakStartDate(rateReqDTO.getBookingStartDate());
        rateForGpnReqDTO.setPeakEndDate(rateReqDTO.getBookingEndDate());
        List<RateResDTO> rateForGpn = getRateForGpn(rateForGpnReqDTO);
        if (CollectionUtil.isEmpty(rateForGpn)) {
            throw exception(ErrorCodeConstants.FINANCE_DATA_ERROR);
        }
        RateResDTO rateResDTO = rateForGpn.get(0);
        List<DateTime> bookingDates = DateUtil.rangeToList(rateReqDTO.getBookingStartDate(),
                rateReqDTO.getBookingEndDate(), DateField.DAY_OF_YEAR);
        List<RateResDTO> resultRates = new ArrayList<>();
        // 结果集
        bookingDates.stream().forEach(bookingDate -> {
            RateResDTO result = new RateResDTO();
            BeanUtils.copyProperties(rateResDTO, result);
            result.setBookingDate(bookingDate);
            resultRates.add(result);
        });
        log.info("[财务费率服务]获取HR系统费率出参：{}", JSONObject.toJSONString(resultRates));
        return resultRates;
    }

    /**
     * 得到gds附加费
     *
     * @param peakStartDate     峰开始日期
     * @param countryRegionCode 国家地区代码
     * @param slCode            sl代码
     * @param sslCode           ssl代码
     * @param peakEndDate
     * @return {@link DictSurchargeRateGdsExternalDO}
     */
    private DictSurchargeRateGdsExternalDO getGdsExternaSurcharge(String peakStartDate, String peakEndDate, String countryRegionCode, String slCode, String sslCode) {
        LambdaQueryWrapper<DictSurchargeRateGdsExternalDO> wrapperX = new LambdaQueryWrapperX<DictSurchargeRateGdsExternalDO>()
                .eq(DictSurchargeRateGdsExternalDO::getCountryRegion, countryRegionCode)
                .eq(DictSurchargeRateGdsExternalDO::getEngagementSlCode, slCode);
        if (setGdsSurchargeWrapper(wrapperX, peakStartDate, peakEndDate, countryRegionCode, slCode, sslCode)) {

            List<DictSurchargeRateGdsExternalDO> dictSurchargeRateGdsExternalDOS = dictSurchargeRateGdsExternalMapper.selectList(wrapperX);
            if (CollectionUtil.isEmpty(dictSurchargeRateGdsExternalDOS)) {
                return null;
            }
            List<DictSurchargeRateGdsExternalDO> resultList = dictSurchargeRateGdsExternalDOS.parallelStream()
                    .sorted(Comparator.comparing(DictSurchargeRateGdsExternalDO::getPeakStartDate).reversed())
                    .collect(Collectors.toList());
            return resultList.get(0);
        }
        return null;
    }


    /**
     * 得到gds附加费
     *
     * @param peakStartDate     峰开始日期
     * @param countryRegionCode 国家地区代码
     * @param slCode            sl代码
     * @param sslCode           ssl代码
     * @param peakEndDate
     * @return {@link DictSurchargeRateGdsInternalDO}
     */
    private DictSurchargeRateGdsInternalDO getGdsInternaSurcharge(String peakStartDate, String peakEndDate, String countryRegionCode, String slCode, String sslCode) {
        LambdaQueryWrapper<DictSurchargeRateGdsInternalDO> wrapperX;
        wrapperX = new LambdaQueryWrapperX<DictSurchargeRateGdsInternalDO>()
                .eq(DictSurchargeRateGdsInternalDO::getCountryRegion, countryRegionCode)
                .eq(DictSurchargeRateGdsInternalDO::getEngagementSlCode, slCode);
        if (setGdsSurchargeWrapper(wrapperX, peakStartDate, peakEndDate, countryRegionCode, slCode, sslCode)) {
            List<DictSurchargeRateGdsInternalDO> dictSurchargeRateGdsInternalDOS = dictSurchargeRateGdsInternalMapper.selectList(wrapperX);
            if (CollectionUtil.isEmpty(dictSurchargeRateGdsInternalDOS)) {
                return null;
            }
            List<DictSurchargeRateGdsInternalDO> resultList = dictSurchargeRateGdsInternalDOS.parallelStream()
                    .sorted(Comparator.comparing(DictSurchargeRateGdsInternalDO::getPeakStartDate).reversed())
                    .collect(Collectors.toList());
            return resultList.get(0);
        }
        return null;
    }

    /**
     * 得到公司附加费
     *
     * @param countryRegionCode 国家地区代码
     * @param empSmuCode        emp smu代码
     * @param peakStartDate     峰开始日期
     * @param peakEndDate       高峰结束日期
     * @return {@link DictSurchargeRateFirmDO}
     */
    private DictSurchargeRateFirmDO getFirmSurcharge(String countryRegionCode, String empSmuCode, String peakStartDate, String peakEndDate) {
        log.info("[财务费率服务]开始获取FIRM附加费系数：{}, {}, {}, {}", countryRegionCode, empSmuCode, peakStartDate, peakEndDate);
        DictSurchargeRateFirmDO dictSurchargeRateFirmDO = dictSurchargeRateFirmMapper.selectOne(new LambdaQueryWrapperX<DictSurchargeRateFirmDO>()
                .eq(DictSurchargeRateFirmDO::getCountryRegion, countryRegionCode)
                .eq(DictSurchargeRateFirmDO::getStatus, 0)
                .eq(DictSurchargeRateFirmDO::getEmployeeSmuCode, empSmuCode)
                .apply("{0} >= peak_start_date ", peakStartDate)
                .apply("{0} < peak_end_date ", peakEndDate)
                .or(wp -> wp.apply("{0} >= peak_start_date and peak_end_date is null", peakStartDate))
                .orderByDesc(DictSurchargeRateFirmDO::getPeakStartDate)
                .last("LIMIT 1"));
        log.info("[财务费率服务]FIRM附加费系数查询结果：{}", JSONObject.toJSONString(dictSurchargeRateFirmDO));
        return dictSurchargeRateFirmDO;
    }


    /**
     * 获得成本率附加费
     *
     * @param fiscalYear 财政年度
     * @param empSmuCode emp smu代码
     * @return {@link DictCostRateSurchargeDO}
     */
    private DictCostRateSurchargeDO getCostRateSurcharge(String fiscalYear, String empSmuCode) {

        DictCostRateSurchargeDO dictCostRateSurchargeDO = dictCostRateSurchargeMapper
                .selectOne(new LambdaQueryWrapperX<DictCostRateSurchargeDO>().eq(DictCostRateSurchargeDO::getFiscalYear, fiscalYear)
                        .eq(DictCostRateSurchargeDO::getSmuCode, empSmuCode).last("LIMIT 1"));

        return null;
    }


    /**
     * 得到gds附加费
     *
     * @param countryRegionCode 国家地区代码
     * @param slCode            sl代码
     * @param sslCode           ssl代码
     * @param wrapperX
     * @return {@link Boolean}
     */
    private Boolean setGdsSurchargeWrapper(LambdaQueryWrapper wrapperX, String peakStartDate,
                                           String peakEndDate, String countryRegionCode, String slCode, String sslCode) {
        wrapperX.apply("{0} >= peak_start_date ", peakStartDate);
        wrapperX.apply("{0} < peak_end_date ", peakEndDate);
        wrapperX.or();
        wrapperX.apply("({0} >= peak_start_date and peak_end_date is null)", peakStartDate);
        String key = StrUtil.concat(true, countryRegionCode, slCode);
        String keyAll = StrUtil.concat(true, countryRegionCode, slCode, sslCode);
        Boolean result = Boolean.FALSE;
        for (RuleSortEnum ruleSortEnum : RuleSortEnum.values()) {
            List<String> rules = ruleSortEnum.getRules();
            if (rules.contains(keyAll)) {
                wrapperX.eq("engagement_sl_code", sslCode);
                wrapperX.last(ruleSortEnum.getSortColumns());
                result = Boolean.TRUE;
                break;
            }
            if (rules.contains(key)) {
                wrapperX.last(ruleSortEnum.getSortColumns());
                result = Boolean.TRUE;
                break;
            }
        }
        return result;
    }


    /**
     * 得到公司配置
     * 获取公司配置成本率
     *
     * @param rateConfigDTO 公司成本率配置dto
     * @return {@link List}<{@link RateResDTO}>
     */
    private List<RateResDTO> getFirmRate(RateConfigDTO rateConfigDTO) {
        log.info("[财务费率服务]根据配置获取费率入参：{}", JSONObject.toJSONString(rateConfigDTO));
        List<DictFirmBillRateDO> firmBillRate = getFirmBillRate(rateConfigDTO);
        log.info("[财务费率服务]查询Bill Rate结果：{}", JSONObject.toJSONString(firmBillRate));
        List<DictFirmBillRateDO> firmNewBillRate = new ArrayList<>();
        List<RateResDTO> rateDTOS = new ArrayList<>();
        // 校验特殊情况
        List<DictFirmBillRateDO> firmBillRateDOS = firmBillRate.stream().filter(bill -> bill.getPriority() == 0).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(firmBillRateDOS)) {
            firmNewBillRate.add(firmBillRateDOS.get(0));
        } else {
            if (CollectionUtil.isNotEmpty(firmBillRate)) {
                firmNewBillRate.add(firmBillRate.get(0));
            }
        }
        // 没有兜底 bill rate
        if (CollectionUtil.isEmpty(firmNewBillRate)) {
            log.info("[财务费率服务]无兜底bill rate：{}", JSONObject.toJSONString(rateConfigDTO));
            firmNewBillRate.add(new DictFirmBillRateDO());
        }
        log.info("[财务费率服务]处理之后的Bill Rate：{}", JSONObject.toJSONString(firmNewBillRate));
        DictFirmCostRateDO costRate = getFirmCostRate(rateConfigDTO);
        DictLongTermRateDO longTermRate = getFirmLongTermRate(rateConfigDTO);
        // 开始时间 结束时间
        String peakStartDate = DateUtil.formatDate(rateConfigDTO.getPeakStartDate());
        String peakEndDate = DateUtil.formatDate(rateConfigDTO.getPeakEndDate());
        DictSurchargeRateFirmDO firmSurcharge = getFirmSurcharge(rateConfigDTO.getCountryRegionCode(), rateConfigDTO.getSmuCode(), peakStartDate, peakEndDate);
        firmNewBillRate.stream().forEach(bill -> {
            RateResDTO rateResDTO = FinanceRateConvert.INSTANCE.convert2RateResDTO(bill);
            if (ObjectUtil.isNotEmpty(costRate)) {
                BeanUtil.copyProperties(costRate, rateResDTO);
            }
            if (ObjectUtil.isNotEmpty(longTermRate)) {
                rateResDTO.setLongTermRate(longTermRate.getLongTermRate());
            }
            if (ObjectUtil.isNotEmpty(firmSurcharge)) {
                rateResDTO.setSurchargeRate(firmSurcharge.getSurchargeRate());
            }
            rateDTOS.add(rateResDTO);
        });
        log.info("[财务费率服务]根据配置获取费率出参：{}", JSONObject.toJSONString(rateDTOS));
        return rateDTOS;
    }


    /**
     * 通过gpn获取公司成本率
     *
     * @param rateForGpnReqDTO 率gpn点播dto
     * @return {@link List}<{@link RateResDTO}>
     */
    private List<RateResDTO> getFirmRateForGpn(RateForGpnReqDTO rateForGpnReqDTO, AdminUserDO adminUserDO) {
        // 开始时间 结束时间
        String peakStartDate = DateUtil.formatDate(rateForGpnReqDTO.getPeakStartDate());
        String peakEndDate = DateUtil.formatDate(rateForGpnReqDTO.getPeakEndDate());
        RateResDTO rateResDTO = getHrRateForGpn(rateForGpnReqDTO.getGpn(), peakStartDate, peakEndDate);
        rateResDTO.setCodeBlockCode(adminUserDO.getCodeBlock());
        String gradeId = StrUtil.toString(adminUserDO.getGradeId());
        RateConfigDTO rateConfigDTO = RateConfigDTO.builder().fiscalYear(rateForGpnReqDTO.getFiscalYear()).rankGrade(gradeId)
                .countryRegionCode(adminUserDO.getCountry()).mgmtRegion(adminUserDO.getMgmtRegion()).slCode(adminUserDO.getServiceLine())
                .sslCode(adminUserDO.getSubServiceLine()).smuCode(adminUserDO.getSubMgmtUnit())
                .competencyCode(adminUserDO.getCompetency()).peakStartDate(rateForGpnReqDTO.getPeakStartDate()).peakEndDate(rateForGpnReqDTO.getPeakEndDate()).build();
        log.info("[财务费率服务]构建配置查询费率入参：{}", JSONObject.toJSONString(rateConfigDTO));
        List<RateResDTO> firmRateForConfig = getFirmRate(rateConfigDTO);
        // 无兜底
        if (ObjectUtil.isEmpty(rateResDTO)) {
            // 重写 code block
            firmRateForConfig.stream().forEach(rate -> {
                rate.setGpn(adminUserDO.getGpn());
                rate.setCodeBlockCode(adminUserDO.getCodeBlock());
            });
            log.info("[财务费率服务]无兜底BILL rate：{}", JSONObject.toJSONString(rateResDTO));
            return firmRateForConfig;
        }
        RateResDTO rateResDTOConfig = new RateResDTO();
        if (CollectionUtil.isNotEmpty(firmRateForConfig)) {
            rateResDTOConfig = firmRateForConfig.get(0);
        }
        // 无 bill rate
        if (!NumberUtil.isGreater(rateResDTO.getBillingRate(), BigDecimal.ZERO)) {
            log.info("[财务费率服务]无BILL rate：{}", JSONObject.toJSONString(rateResDTO));
            rateResDTO.setBillingRate(rateResDTOConfig.getBillingRate());
        }
        if (!NumberUtil.isGreater(rateResDTO.getCostRate(), BigDecimal.ZERO)) {
            log.info("[财务费率服务]无Cost rate：{}", JSONObject.toJSONString(rateResDTO));
            rateResDTO.setCostRate(rateResDTOConfig.getCostRate());
        }
        rateResDTO.setCurrency(rateResDTOConfig.getCurrency());
        rateResDTO.setCostPlusRate(rateResDTOConfig.getCostPlusRate());
        rateResDTO.setLongTermRate(rateResDTOConfig.getLongTermRate());
        rateResDTO.setNetStandardRate(rateResDTOConfig.getNetStandardRate());
        rateResDTO.setSurchargeRate(rateResDTOConfig.getSurchargeRate());
        return Arrays.asList(rateResDTO);
    }


    /**
     * 得到gds率
     *
     * @param userCodeBlockCode 用户代码块代码
     * @param rateForGpnReqDTO  率gpn点播dto
     * @return {@link List}<{@link RateResDTO}>
     */
    private List<RateResDTO> getGdsRate(String userCodeBlockCode, RateForGpnReqDTO rateForGpnReqDTO) {
        log.info("[财务费率服务]获取GDS费率入参：{}", JSONObject.toJSONString(rateForGpnReqDTO));
        CodeBlockDO userCodeBlock = getCodeBlock(userCodeBlockCode);
        CodeBlockDO engCodeBlock = getCodeBlock(rateForGpnReqDTO.getEngCodeBlockCode());
        // 开始时间 结束时间
        String peakStartDate = DateUtil.formatDate(rateForGpnReqDTO.getPeakStartDate());
        String peakEndDate = DateUtil.formatDate(rateForGpnReqDTO.getPeakEndDate());
        RateResDTO rateResDTO = getHrRateForGpn(rateForGpnReqDTO.getGpn(), peakStartDate, peakEndDate);
        rateResDTO.setCodeBlockCode(userCodeBlock.getCodeBlockCode());
        rateResDTO.setBillingRate(rateResDTO.getCostRate());
        if (!NumberUtil.isGreater(rateResDTO.getCostRate(), BigDecimal.ZERO)) {
            DictGdsRateValueDO gdsCostRate = getGdsCostRate(userCodeBlockCode, FinanceRateConvert.INSTANCE.convertGdsRateConfigDTO(rateForGpnReqDTO));
            if (ObjectUtil.isNotEmpty(gdsCostRate)) {
                rateResDTO = FinanceRateConvert.INSTANCE.convert3RateResDTO(gdsCostRate);
            }
        }
        DictFirmCostRateDO gdsNsrRate = getGdsNsrRate(userCodeBlockCode, rateForGpnReqDTO.getEngCodeBlockCode(), rateForGpnReqDTO);
        if (ObjectUtil.isNotEmpty(gdsNsrRate)) {
            rateResDTO.setNetStandardRate(gdsNsrRate.getNetStandardRate());
        }
        DictSurchargeRateFirmDO firmSurcharge = getFirmSurcharge(peakStartDate,
                engCodeBlock.getCountryRegionCode(), engCodeBlock.getSlCode(), engCodeBlock.getSslCode());
        // by 国内 gds
        if (StrUtil.contains(userCodeBlock.getBuCode(), "XE")) {
            DictSurchargeRateGdsInternalDO gdsInternaSurcharge = getGdsInternaSurcharge(peakStartDate, peakEndDate,
                    engCodeBlock.getCountryRegionCode(), engCodeBlock.getSlCode(), engCodeBlock.getSslCode());
            rateResDTO.setFirmSurchargeRate(ObjectUtil.isEmpty(firmSurcharge) ? null : firmSurcharge.getSurchargeRate());
            rateResDTO.setGdsSurhargeRate(ObjectUtil.isEmpty(gdsInternaSurcharge) ? null : gdsInternaSurcharge.getSurchargeRate());
            rateResDTO.setSurchargeRate(NumberUtil.add(rateResDTO.getFirmSurchargeRate(), rateResDTO.getGdsSurhargeRate()));
        } else {
            // 海外 gds
            DictSurchargeRateGdsExternalDO gdsExternaSurcharge = getGdsExternaSurcharge(peakStartDate, peakEndDate,
                    engCodeBlock.getCountryRegionCode(), engCodeBlock.getSlCode(), engCodeBlock.getSslCode());
            rateResDTO.setFirmSurchargeRate(ObjectUtil.isEmpty(firmSurcharge) ? null : firmSurcharge.getSurchargeRate());
            rateResDTO.setGdsSurhargeRate(ObjectUtil.isEmpty(gdsExternaSurcharge) ? null : gdsExternaSurcharge.getSurchargeRate());
            rateResDTO.setSurchargeRate(NumberUtil.add(rateResDTO.getFirmSurchargeRate(), rateResDTO.getGdsSurhargeRate()));
        }
        log.info("[财务费率服务]获取GDS费率出参：{}", JSONObject.toJSONString(rateResDTO));
        return Arrays.asList(rateResDTO);
    }

    /**
     * 获得成本率
     *
     * @param rateConfigDTO 公司配置dto率
     * @return {@link DictFirmCostRateDO}
     */
    private DictFirmCostRateDO getFirmCostRate(RateConfigDTO rateConfigDTO) {
        //获取grade Leavel
        DictGradeLevelDTO gradeLeavel = getGradeLeavel(rateConfigDTO.getRankGrade());
        // cost rate
        DictFirmCostRateDO dictFirmCostRateDO = dictFirmCostRateMapper.selectOne(new LambdaQueryWrapperX<DictFirmCostRateDO>()
                .eq(DictFirmCostRateDO::getFiscalYear, rateConfigDTO.getFiscalYear())
                .eq(DictFirmCostRateDO::getCountryRegion, rateConfigDTO.getCountryRegionCode())
                .eq(DictFirmCostRateDO::getActivityType, gradeLeavel.getActivityType())
                .eq(DictFirmCostRateDO::getStatus, 0)
                .and(wp -> wp.eq(DictFirmCostRateDO::getMrCode, rateConfigDTO.getMgmtRegion()).or()
                        .eq(DictFirmCostRateDO::getMrCode, ALL))
                .and(wp -> wp.eq(DictFirmCostRateDO::getSlCode, rateConfigDTO.getSlCode()).or()
                        .eq(DictFirmCostRateDO::getSlCode, ALL))
                .and(wp -> wp.eq(DictFirmCostRateDO::getSslCode, rateConfigDTO.getSslCode()).or()
                        .eq(DictFirmCostRateDO::getSslCode, ALL))
                .and(wp -> wp.eq(DictFirmCostRateDO::getCompetencyCode, rateConfigDTO.getCompetencyCode()).or()
                        .eq(DictFirmCostRateDO::getCompetencyCode, ALL))
                .and(wp -> wp.eq(DictFirmCostRateDO::getSmuCode, rateConfigDTO.getSmuCode()).or()
                        .eq(DictFirmCostRateDO::getSmuCode, ALL)).orderByDesc(DictFirmCostRateDO::getPriority)
                .last("LIMIT 1"));
        log.info("[财务费率服务]获取Firm Cost相关费率1：{}", JSONObject.toJSONString(dictFirmCostRateDO));
        return dictFirmCostRateDO;
    }

    /**
     * 得到长期利率
     *
     * @param rateConfigDTO 公司配置dto率
     * @return {@link DictLongTermRateDO}
     */
    private DictLongTermRateDO getFirmLongTermRate(RateConfigDTO rateConfigDTO) {
        // query grade leavel
        DictGradeLevelDTO gradeLeavel = getGradeLeavel(rateConfigDTO.getRankGrade());

        // long term rate
        DictLongTermRateDO dictLongTermRateDO = dictLongTermRateMapper.selectOne(new LambdaQueryWrapperX<DictLongTermRateDO>()
                .eq(DictLongTermRateDO::getFiscalYear, rateConfigDTO.getFiscalYear())
                .eq(DictLongTermRateDO::getCountryRegion, rateConfigDTO.getCountryRegionCode())
                .eq(DictLongTermRateDO::getStatus, 0)
                .eq(DictLongTermRateDO::getActivityType, gradeLeavel.getActivityType())
                .and(wp -> wp.eq(DictLongTermRateDO::getMrCode, rateConfigDTO.getMgmtRegion()).or()
                        .eq(DictLongTermRateDO::getMrCode, ALL))
                .and(wp -> wp.eq(DictLongTermRateDO::getSlCode, rateConfigDTO.getSlCode()).or()
                        .eq(DictLongTermRateDO::getSlCode, ALL))
                .and(wp -> wp.eq(DictLongTermRateDO::getSslCode, rateConfigDTO.getSslCode()).or()
                        .eq(DictLongTermRateDO::getSslCode, ALL))
                .and(wp -> wp.eq(DictLongTermRateDO::getCompetencyCode, rateConfigDTO.getCompetencyCode()).or()
                        .eq(DictLongTermRateDO::getCompetencyCode, ALL))
                .and(wp -> wp.eq(DictLongTermRateDO::getSmuCode, rateConfigDTO.getSmuCode()).or()
                        .eq(DictLongTermRateDO::getSmuCode, ALL)).orderByDesc(DictLongTermRateDO::getPriority)
                .last("LIMIT 1"));
        log.info("[财务费率服务]获取Firm Long Term相关费率：{}", JSONObject.toJSONString(dictLongTermRateDO));
        return dictLongTermRateDO;
    }

    /**
     * 得到公司票据贴现率
     *
     * @param rateConfigDTO 公司配置dto率
     * @return {@link List}<{@link DictFirmBillRateDO}>
     */
    private List<DictFirmBillRateDO> getFirmBillRate(RateConfigDTO rateConfigDTO) {
        // by config
        List<DictFirmBillRateDO> dictFirmBillRateDOS = dictFirmBillRateMapper.selectList(new LambdaQueryWrapperX<DictFirmBillRateDO>()
                .eq(DictFirmBillRateDO::getFiscalYear, rateConfigDTO.getFiscalYear())
                .eq(DictFirmBillRateDO::getCountryRegion, rateConfigDTO.getCountryRegionCode())
                .eq(DictFirmBillRateDO::getGradeCode, rateConfigDTO.getRankGrade())
                .eq(DictFirmBillRateDO::getStatus, 0)
                .and(wp -> wp.eq(DictFirmBillRateDO::getMrCode, rateConfigDTO.getMgmtRegion()).or()
                        .eq(DictFirmBillRateDO::getMrCode, ALL))
                .and(wp -> wp.eq(DictFirmBillRateDO::getSlCode, rateConfigDTO.getSlCode()).or()
                        .eq(DictFirmBillRateDO::getSlCode, ALL))
                .and(wp -> wp.eq(DictFirmBillRateDO::getSslCode, rateConfigDTO.getSslCode()).or()
                        .eq(DictFirmBillRateDO::getSslCode, ALL))
                .and(wp -> wp.eq(DictFirmBillRateDO::getCompetencyCode, rateConfigDTO.getCompetencyCode()).or()
                        .eq(DictFirmBillRateDO::getCompetencyCode, ALL))
                .and(wp -> wp.eq(DictFirmBillRateDO::getSmuCode, rateConfigDTO.getSmuCode()).or()
                        .eq(DictFirmBillRateDO::getSmuCode, ALL)).orderByDesc(DictFirmBillRateDO::getPriority));
        log.info("[财务费率服务]获取Firm Bill Rate相关费率：{}", JSONObject.toJSONString(dictFirmBillRateDOS));
        return dictFirmBillRateDOS;
    }

    /**
     * 获得gpn手动覆盖比率
     *
     * @param gpn gpn
     * @return {@link RateResDTO}
     */
    private RateResDTO getHrRateForGpn(String gpn, String startDate, String endDate) {
        log.info("[财务费率服务]通过GPN获取费率入参：{}", gpn);
        DictHrBillRateDO dictHrBillRateDO = dictHrBillRateMapper.selectOne(new LambdaQueryWrapperX<DictHrBillRateDO>()
                .apply(" LEFT(EffectiveStartDate,10) <= {0}", startDate)
                .eq(DictHrBillRateDO::getGpn, gpn).orderByDesc(DictHrBillRateDO::getEffectivestartdate).last("LIMIT 1"));
        log.info("[财务费率服务]查询HRBillRate：{}", JSONObject.toJSONString(dictHrBillRateDO));
        DictCostRateDO dictCostRateDO = dictCostRateMapper.selectOne(new LambdaQueryWrapperX<DictCostRateDO>()
                .apply(" DATE_FORMAT(DDH_EFFDATE, '%Y-%m-%d') <= {0}", startDate)
                .eq(DictCostRateDO::getGpn, gpn).orderByDesc(DictCostRateDO::getEffectiveDate).last("LIMIT 1"));
        log.info("[财务费率服务]查询HRCostRate：{}", JSONObject.toJSONString(dictCostRateDO));
        log.info("[财务费率服务]通过GPN获取费率查询结果：{}", JSONObject.toJSONString(dictHrBillRateDO));
        RateResDTO rateResDTO = new RateResDTO();
        rateResDTO.setGpn(gpn);
        rateResDTO.setBillingRate(ObjectUtil.isNotEmpty(dictHrBillRateDO) ? dictHrBillRateDO.getMydefbillrate() : BigDecimal.ZERO);
        rateResDTO.setCostRate(ObjectUtil.isNotEmpty(dictCostRateDO) ? dictCostRateDO.getCostRate() : BigDecimal.ZERO);
        rateResDTO.setCurrency(ObjectUtil.isNotEmpty(dictHrBillRateDO) ? dictHrBillRateDO.getCurrencycd() : null);
        return rateResDTO;
    }


    /**
     * 得到gds成本率
     *
     * @param codeBlockCode    代码块代码
     * @param gdsRateConfigDTO gds率配置dto
     * @return {@link DictGdsRateValueDO}
     */
    private DictGdsRateValueDO getGdsCostRate(String codeBlockCode, GdsRateConfigDTO gdsRateConfigDTO) {
        log.info("[财务费率服务]获取GDS COS TRATE费率入参：{}, {}", codeBlockCode, JSONObject.toJSONString(gdsRateConfigDTO));
        Boolean isConfig = StrUtil.isEmpty(codeBlockCode) ? true : false;
        log.info("[财务费率服务]配置类型：{}", isConfig);
        DictGdsRateValueDO dictGdsRateValueDO = dictGdsRateValueMapper.selectOne(new LambdaQueryWrapperX<DictGdsRateValueDO>()
                .eq(DictGdsRateValueDO::getFiscalYear, gdsRateConfigDTO.getFiscalYear())
                .eq(DictGdsRateValueDO::getGradeCode, gdsRateConfigDTO.getRankGrade())
                .eq(DictGdsRateValueDO::getStatus, 0)
                .eq(DictGdsRateValueDO::getPurchasingCountry, gdsRateConfigDTO.getCountryRegionCode())
                .eq(!isConfig, DictGdsRateValueDO::getEmployeeCodeBlock, codeBlockCode)
                .eq(isConfig, DictGdsRateValueDO::getGradeCode, gdsRateConfigDTO.getRankGrade())
                .eq(isConfig, DictGdsRateValueDO::getBuCode, gdsRateConfigDTO.getBuCode())
                .eq(isConfig, DictGdsRateValueDO::getOuCode, gdsRateConfigDTO.getOuCode())
                .eq(isConfig, DictGdsRateValueDO::getMuCode, gdsRateConfigDTO.getMuCode())
                .eq(isConfig, DictGdsRateValueDO::getSmuCode, gdsRateConfigDTO.getSmuCode()).last("LIMIT 1"));
        log.info("[财务费率服务]获取GDS费率查询结果：{}", JSONObject.toJSONString(dictGdsRateValueDO));
        return dictGdsRateValueDO;
    }

    /**
     * 获取 Code Block
     *
     * @param codeBlockCode 代码块代码
     * @return {@link CodeBlockDO}
     */
    private CodeBlockDO getCodeBlock(String codeBlockCode) {
        log.info("[财务费率服务]查询Code Block 入参：{}", codeBlockCode);
        CodeBlockDO codeBlockDO = codeBlockMapper.selectOne(new LambdaQueryWrapperX<CodeBlockDO>()
                .eq(CodeBlockDO::getCodeBlockCode, codeBlockCode));
        if (ObjectUtil.isEmpty(codeBlockDO)) {
            throw exception(ErrorCodeConstants.FINANCE_CODE_BLOCK_ERROR);
        }
        log.info("[财务费率服务]查询Code Block 查询结果：{}", JSONObject.toJSONString(codeBlockDO));
        return codeBlockDO;
    }


    /**
     * 得到gds nsr率
     *
     * @param engCodeBlockCode  eng代码块代码
     * @param userCodeBlockCode 用户代码块代码
     * @param rateForGpnReqDTO  率gpn点播dto
     * @return {@link DictFirmCostRateDO}
     */
    private DictFirmCostRateDO getGdsNsrRate(String userCodeBlockCode, String engCodeBlockCode, RateForGpnReqDTO rateForGpnReqDTO) {
        log.info("[财务费率服务]GDS 获取NSR RATE入参：engCodeBlockCode:{}, userCodeBlockCode:{}", engCodeBlockCode, userCodeBlockCode);
        log.info("[财务费率服务]GDS 获取NSR RATE入参：RateForGpnReqDTO:{}", JSONObject.toJSONString(rateForGpnReqDTO));
        // by ease mode
        if (getGdsEaseMode(engCodeBlockCode, userCodeBlockCode)) {
            log.info("[财务费率服务]进入EASE MODE");
            return getFirmCostRate(FinanceRateConvert.INSTANCE.convert2FirmRateConfigDTO(rateForGpnReqDTO));
        }
        // by none ease mode
        return null;
    }

    /**
     * 验证 gds ease mode
     * true: Ease Mode
     * false None Ease Mode
     *
     * @param engCodeBlockCode  eng代码块代码
     * @param userCodeBlockCode 用户代码块代码
     * @return {@link Boolean}
     */
    private Boolean getGdsEaseMode(String engCodeBlockCode, String userCodeBlockCode) {
        log.info("[财务费率服务] 验证是否为EaseMode模式,项目CodeBlock:{}, 人员CodeBlock", engCodeBlockCode, userCodeBlockCode);
        CodeBlockDO engCodeBlock = getCodeBlock(engCodeBlockCode);
        CodeBlockDO userCodeBlock = getCodeBlock(userCodeBlockCode);
        if (!StrUtil.equals(engCodeBlock.getSourceType(), SourceTypeEnum.GDS.getValue())
                && StrUtil.equals(userCodeBlock.getSourceType(), SourceTypeEnum.GDS.getValue())) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }


    /**
     * gpn获得用户
     *
     * @param gpn gpn
     * @return {@link AdminUserDO}
     */
    private AdminUserDO getUserForGpn(String gpn) {
        log.info("[财务费率服务]获取人员信息入参：{}", gpn);
        AdminUserDO adminUserDO = adminUserService.getUserForGpnOrEmail(gpn, null);
        // check user
        if (ObjectUtil.isEmpty(adminUserDO)) {
            throw exception(ErrorCodeConstants.FINANCE_USER_GPN_ERROR);
        }
        log.info("[财务费率服务]获取人员信息出参：{}", JSONObject.toJSONString(adminUserDO));
        return adminUserDO;
    }


    /**
     * 获得等级leavel
     *
     * @param rankGrade 获得等级leavel
     * @return {@link DictGradeLevelDTO}
     */
    private DictGradeLevelDTO getGradeLeavel(String rankGrade) {
        log.info("[财务费率服务]获得等级leavel入参：{}", rankGrade);
        // activity type
        Map<String, Object> confParams = MapUtil.newHashMap();
        confParams.put("functionType", "default");
        confParams.put("categoryCode", "GradeLevel");
        // query params
        Map<String, Object> queryParams = MapUtil.newHashMap();
        queryParams.put("gradeCode", rankGrade);
        Object dict = findDict(null, confParams, queryParams);
        log.info("[财务费率服务]获得等级leavel查询结果：{}", JSONObject.toJSONString(rankGrade));
        if (ObjectUtil.isEmpty(dict)) {
            throw exception(ErrorCodeConstants.FINANCE_GRADE_LEAVEL_ERROR);
        }
        List<DictGradeLevelDTO> dictGradeLevelDTOs = Convert.convert(new TypeReference<List<DictGradeLevelDTO>>() {
        }, dict);
        return dictGradeLevelDTOs.get(0);
    }


    /**
     * 有效配置
     *
     * @param clazz  clazz
     * @param object 对象
     * @return {@link Boolean}
     */
    private <T> Boolean validConfig(Class<T> clazz, T object) {
        if (ObjectUtil.isEmpty(object)) {
            throw exception(ErrorCodeConstants.FINANCE_REQUEST_PARAMETER_ERROR);
        }
        log.info("[财务费率服务]检查配置信息是否存在空值入参：{}", JSONObject.toJSONString(object));
        Boolean result = Boolean.TRUE;
        for (Field field : ReflectUtil.getFields(clazz)) {
            Object fieldValue = ReflectUtil.getFieldValue(object, field);
            log.info("[财务费率服务]检查属性值：{}, {}", field.getName(), JSONObject.toJSONString(fieldValue));
            if (ObjectUtil.isEmpty(fieldValue)) {
                log.info("[财务费率服务]检查属性值不通过：{}, {}", field.getName(), JSONObject.toJSONString(fieldValue));
                result = Boolean.FALSE;
                break;
            }
        }
        log.info("[财务费率服务]检查配置信息是否存在空值出参：{}", JSONObject.toJSONString(result));
        return result;
    }

    /**
     * 查找字典
     *
     * @param config      配置
     * @param confParams  配置参数
     * @param queryParams 查询参数
     * @return {@link Object}
     */
    private Object findDict(Map<String, Object> config, Map<String, Object> confParams, Map<String, Object> queryParams) {
        log.info("[财务费率服务]查找字典入参：{}, {}", JSONObject.toJSONString(confParams), JSONObject.toJSONString(queryParams));
        //构建初始化数据
        if (MapUtil.isEmpty(config)) {
            config = MapUtil.newHashMap();
        }
        if (MapUtil.isEmpty(queryParams)) {
            queryParams = MapUtil.newHashMap();
        }
        R invoke = categoryRequestHandler.invoke(null, null, null, config, confParams, queryParams);
        log.info("[财务费率服务]查找字典出参：{}", JSONObject.toJSONString(invoke));
        return invoke.getData();
    }

}
