package com.smppw.core;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import com.smppw.common.pojo.IStrategy;
import com.smppw.common.pojo.IndexesTradeDateDo;
import com.smppw.common.pojo.dto.CompoundRet;
import com.smppw.common.pojo.dto.DateValue;
import com.smppw.common.pojo.dto.calc.IndicatorCalcPropertyDto;
import com.smppw.common.pojo.dto.calc.IndicatorCalcReqPropertyDto;
import com.smppw.common.pojo.dto.calc.IndicatorCalcTimeRangeDto;
import com.smppw.common.pojo.dto.indicator.CalcMultipleSecMultipleTimeRangeIndicatorReq;
import com.smppw.common.pojo.dto.indicator.DateIntervalDto;
import com.smppw.common.pojo.enums.*;
import com.smppw.utils.DateIntervalUtil;
import com.smppw.utils.IndicatorCalcUtil;
import com.smppw.utils.NavUtil;
import com.smppw.utils.TradeDateUtil;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author wangzaijun
 * @date 2023/8/2 10:17
 * @description 指标计算和走势图
 */
public class IndicatorService {
    private IndicatorService() {
    }

    public static IndicatorService getInstance() {
        return IndicatorService.IndicatorServiceBuilder.INSTANCE;
    }

    public Map<String, List<IndicatorCalcPropertyDto>> getMultipleSecTrend(List<String> mainSecIdList, Map<String, String> secBenchmarkIdMap, List<String> indexIdList,
                                                                           DateIntervalDto dateIntervalDto, Frequency dataFrequency, BigDecimal fixedIncome, BigDecimal initValue,
                                                                           List<TrendType> trendTypeV2List, Map<String, List<DateValue>> allNavMap) {
        Map<String, List<IndicatorCalcPropertyDto>> indicatorCalcPropertyDtoListMap = new HashMap<>();
        for (String mainSecId : mainSecIdList) {
            String benchmarkId = secBenchmarkIdMap.get(mainSecId);

            Map<String, List<DateValue>> secAllNavMap = new HashMap();
            secAllNavMap.put(mainSecId, NavUtil.getNewNavList(allNavMap.get(mainSecId)));
            secAllNavMap.put(benchmarkId, NavUtil.getNewNavList(allNavMap.get(benchmarkId)));
            for (String indexId : indexIdList) {
                secAllNavMap.put(indexId, NavUtil.getNewNavList(allNavMap.get(indexId)));
            }

            List<DateValue> mainSecIdNavList = secAllNavMap.get(mainSecId);

            String theFirstNavDate = "";
            String theLastNavDate = "";
            if (mainSecIdNavList != null && mainSecIdNavList.size() > 0) {
                theFirstNavDate = mainSecIdNavList.get(0).getDate();
                theLastNavDate = mainSecIdNavList.get(mainSecIdNavList.size() - 1).getDate();
            }

            List<IndexesTradeDateDo> indexesTradeDateDoList = TradeDateUtil.listInfoByDateInterval(theFirstNavDate, theLastNavDate);

            Frequency navFrequency = dataFrequency == null || dataFrequency == Frequency.Default ? Frequency.Daily : dataFrequency;

            Frequency calcFrequency = dataFrequency;

            List<DateValue> validMainSecNavList = NavUtil.filterFrequencyNav(mainSecIdNavList, indexesTradeDateDoList, navFrequency);
            secAllNavMap.put(mainSecId, validMainSecNavList);

            List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(dateIntervalDto, validMainSecNavList, indexesTradeDateDoList
                    , dateIntervalDto.getId());

            List<IndicatorCalcPropertyDto> indicatorCalcPropertyDtoList = new ArrayList<>();

            for (IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto : indicatorCalcTimeRangeDtoList) {

                Map<String, List<DateValue>> dateSecAllNavMap = new HashMap();
                dateSecAllNavMap.put(mainSecId, NavUtil.getNewNavList(secAllNavMap.get(mainSecId)));
                dateSecAllNavMap.put(benchmarkId, NavUtil.getNewNavList(secAllNavMap.get(benchmarkId)));
                for (String indexId : indexIdList) {
                    dateSecAllNavMap.put(indexId, NavUtil.getNewNavList(secAllNavMap.get(indexId)));
                }

                IndicatorCalcReqPropertyDto indicatorCalcReq = prepareTrendReq(mainSecId, benchmarkId, indexIdList, indicatorCalcTimeRangeDto,
                        dateSecAllNavMap, calcFrequency, calcFrequency, trendTypeV2List, fixedIncome, initValue);
                if (validMainSecNavList != null && validMainSecNavList.size() > 0) {
                    IndicatorCalcPropertyDto trend = CalcService.getInstance().getTrend(indicatorCalcReq);
                    indicatorCalcPropertyDtoList.add(trend);
                } else {
                    IndicatorCalcPropertyDto indicatorCalcPropertyDto = new IndicatorCalcPropertyDto();
                    indicatorCalcPropertyDto.setIndicatorCalcReq(indicatorCalcReq);
                    indicatorCalcPropertyDtoList.add(indicatorCalcPropertyDto);
                }
            }
            indicatorCalcPropertyDtoListMap.put(mainSecId, indicatorCalcPropertyDtoList);
        }
        return indicatorCalcPropertyDtoListMap;
    }

    public Map<String, List<IndicatorCalcPropertyDto>> calcMultipleSecMultipleTimeRangeIndicator(CalcMultipleSecMultipleTimeRangeIndicatorReq req, Map<String, List<DateValue>> allNavMap) {

        List<String> mainSecIdList = req.getMainSecIdList();
        Map<String, String> secBenchmarkIdMap = req.getSecBenchmarkIdMap();
        List<String> indexIdList = req.getIndexIdList();
        RaiseType raiseType = req.getRaiseType();
        IStrategy strategy = req.getStrategy();
        Map<String, List<DateIntervalDto>> secDateIntervalDtoListMap = req.getSecDateIntervalDtoListMap();
        Frequency dataFrequency = req.getDataFrequency();
        List<Indicator> indicatorList = req.getIndicatorList();
        List<Indicator> geoExtraindicatorList = req.getGeoExtraindicatorList();
        String riskOfFreeId = req.getRiskOfFreeId();
        Double riskOfFreeValue = req.getRiskOfFreeValue();
        boolean ifAnnualize = req.isIfAnnualize();
        boolean calcIndexRetIndicatorValue = req.isCalcIndexRetIndicatorValue();
        boolean needRet = req.isNeedRet();
        boolean needExtraRet = req.isNeedExtraRet();
        boolean ifConvertPerformanceConsistencyWord = req.isIfConvertPerformanceConsistencyWord();

        Frequency navFrequency = dataFrequency == null || dataFrequency == Frequency.Default ? Frequency.Daily : dataFrequency;

        Map<String, List<IndicatorCalcPropertyDto>> indicatorCalcPropertyDtoListMap = new HashMap<>();
        for (String mainSecId : mainSecIdList) {

            String benchmarkId = secBenchmarkIdMap.get(mainSecId);
            Map<String, List<DateValue>> secAllNavMap = new HashMap();
            secAllNavMap.put(mainSecId, NavUtil.getNewNavList(allNavMap.get(mainSecId)));
            secAllNavMap.put(benchmarkId, NavUtil.getNewNavList(allNavMap.get(benchmarkId)));
            for (String indexId : indexIdList) {
                secAllNavMap.put(indexId, NavUtil.getNewNavList(allNavMap.get(indexId)));
            }
            if (StringUtils.isNotEmpty(riskOfFreeId)) {
                secAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(allNavMap.get(riskOfFreeId)));
            }

            List<IndicatorCalcPropertyDto> indicatorCalcPropertyDtoList = new ArrayList<>();

            List<DateValue> mainSecIdNavList = secAllNavMap.get(mainSecId);

            String theFirstNavDate = "";
            String theLastNavDate = "";
            if (mainSecIdNavList != null && mainSecIdNavList.size() > 0) {
                theFirstNavDate = mainSecIdNavList.get(0).getDate();
                theLastNavDate = mainSecIdNavList.get(mainSecIdNavList.size() - 1).getDate();
            }
            List<IndexesTradeDateDo> indexesTradeDateDoList = TradeDateUtil.listInfoByDateInterval(theFirstNavDate, theLastNavDate);

            List<DateValue> validMainSecNavList = NavUtil.filterFrequencyNav(mainSecIdNavList, indexesTradeDateDoList, navFrequency);
            secAllNavMap.put(mainSecId, validMainSecNavList);

            Frequency calcFrequency = dataFrequency;

            List<DateIntervalDto> dateIntervalDtoList = new ArrayList<>();
            if (secDateIntervalDtoListMap != null && secDateIntervalDtoListMap.get(mainSecId) != null) {
                dateIntervalDtoList = secDateIntervalDtoListMap.get(mainSecId);
            }

            for (DateIntervalDto dateIntervalDto : dateIntervalDtoList) {

                rollFindStartDate(mainSecId, dateIntervalDto, indexesTradeDateDoList, allNavMap, calcFrequency);

                List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(dateIntervalDto, validMainSecNavList, indexesTradeDateDoList
                        , dateIntervalDto.getId());

                for (IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto : indicatorCalcTimeRangeDtoList) {

                    Map<String, List<DateValue>> dateSecAllNavMap = new HashMap();
                    dateSecAllNavMap.put(mainSecId, NavUtil.getNewNavList(secAllNavMap.get(mainSecId)));
                    dateSecAllNavMap.put(benchmarkId, NavUtil.getNewNavList(secAllNavMap.get(benchmarkId)));
                    for (String indexId : indexIdList) {
                        dateSecAllNavMap.put(indexId, NavUtil.getNewNavList(secAllNavMap.get(indexId)));
                    }
                    if (StringUtils.isNotEmpty(riskOfFreeId)) {
                        dateSecAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(secAllNavMap.get(riskOfFreeId)));
                    }

                    IndicatorCalcReqPropertyDto indicatorCalcReq = prepareIndicatorCalcReq(mainSecId, benchmarkId, indexIdList, indicatorCalcTimeRangeDto,
                            dateSecAllNavMap, riskOfFreeId, riskOfFreeValue, null, calcFrequency, calcFrequency, indicatorList, geoExtraindicatorList, ifAnnualize, calcIndexRetIndicatorValue,
                            needRet, needExtraRet, ifConvertPerformanceConsistencyWord);

                    IndicatorCalcPropertyDto indicatorCalcPropertyDto = CalcService.getInstance().indicatorCalc(indicatorCalcReq);
                    indicatorCalcPropertyDtoList.add(indicatorCalcPropertyDto);

                }
            }

            indicatorCalcPropertyDtoListMap.put(mainSecId, indicatorCalcPropertyDtoList);
        }


        return indicatorCalcPropertyDtoListMap;
    }

    public Map<String, Map<Frequency, List<IndicatorCalcPropertyDto>>> calcMultipleSecMultipleTimeRangeIndicator(List<String> mainSecIdList, Map<String, String> secBenchmarkIdMap, List<String> indexIdList,
                                                                                                                 List<DateIntervalDto> dateIntervalDtoList,
                                                                                                                 List<Frequency> dataFrequencyList, Map<Frequency, List<Indicator>> dataFrequencyIndicatorListMap,
                                                                                                                 Map<Frequency, List<Indicator>> dataFrequencyGeoExtraIndicatorListMap, String riskOfFreeId, Double riskOfFreeValue,
                                                                                                                 boolean ifAnnualize, boolean calcIndexRetIndicatorValue, boolean needRet, boolean needExtraRet, int leastRetNum, Map<String, List<DateValue>> allNavMap) {
        Map<String, Map<Frequency, List<IndicatorCalcPropertyDto>>> indicatorCalcPropertyDtoListMapMap = new HashMap<>();
        for (String mainSecId : mainSecIdList) {

            String benchmarkId = secBenchmarkIdMap.get(mainSecId);

            Map<Frequency, List<IndicatorCalcPropertyDto>> indicatorCalcPropertyDtoListMap = new HashMap<>();
            for (Frequency dataFrequency : dataFrequencyList) {
                Map<String, List<DateValue>> secAllNavMap = new HashMap();
                secAllNavMap.put(mainSecId, NavUtil.getNewNavList(allNavMap.get(mainSecId)));
                secAllNavMap.put(benchmarkId, NavUtil.getNewNavList(allNavMap.get(benchmarkId)));
                for (String indexId : indexIdList) {
                    secAllNavMap.put(indexId, NavUtil.getNewNavList(allNavMap.get(indexId)));
                }
                if (StringUtils.isNotEmpty(riskOfFreeId)) {
                    secAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(allNavMap.get(riskOfFreeId)));
                }

                Frequency navFrequency = dataFrequency == null || dataFrequency == Frequency.Default ? Frequency.Daily : dataFrequency;
                List<DateValue> mainSecIdNavList = secAllNavMap.get(mainSecId);

                String theFirstNavDate = "";
                String theLastNavDate = "";
                if (mainSecIdNavList != null && mainSecIdNavList.size() > 0) {
                    theFirstNavDate = mainSecIdNavList.get(0).getDate();
                    theLastNavDate = mainSecIdNavList.get(mainSecIdNavList.size() - 1).getDate();
                }

                List<IndexesTradeDateDo> indexesTradeDateDoList = TradeDateUtil.listInfoByDateInterval(theFirstNavDate, theLastNavDate);

                List<DateValue> validMainSecNavList = NavUtil.filterFrequencyNav(mainSecIdNavList, indexesTradeDateDoList, navFrequency);
                secAllNavMap.put(mainSecId, validMainSecNavList);

                Frequency calcFrequency = dataFrequency;

                List<Indicator> indicatorList = dataFrequencyIndicatorListMap.get(dataFrequency);
                List<Indicator> geoExtraIndicatorList = dataFrequencyGeoExtraIndicatorListMap.get(dataFrequency);

                List<IndicatorCalcPropertyDto> indicatorCalcPropertyDtoList = new ArrayList<>();
                for (DateIntervalDto dateIntervalDto : dateIntervalDtoList) {

                    rollFindStartDate(mainSecId, dateIntervalDto, indexesTradeDateDoList, allNavMap, calcFrequency);

                    List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(dateIntervalDto, validMainSecNavList, indexesTradeDateDoList
                            , dateIntervalDto.getId());

                    for (IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto : indicatorCalcTimeRangeDtoList) {

                        Map<String, List<DateValue>> dateSecAllNavMap = new HashMap();
                        dateSecAllNavMap.put(mainSecId, NavUtil.getNewNavList(secAllNavMap.get(mainSecId)));
                        dateSecAllNavMap.put(benchmarkId, NavUtil.getNewNavList(secAllNavMap.get(benchmarkId)));
                        for (String indexId : indexIdList) {
                            dateSecAllNavMap.put(indexId, NavUtil.getNewNavList(secAllNavMap.get(indexId)));
                        }
                        if (StringUtils.isNotEmpty(riskOfFreeId)) {
                            dateSecAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(secAllNavMap.get(riskOfFreeId)));
                        }

                        if (validMainSecNavList != null && validMainSecNavList.size() > 0 &&
                                (indicatorCalcTimeRangeDto.getStartDate().compareTo(validMainSecNavList.get(0).getDate()) >= 0 || indicatorCalcTimeRangeDto.getTimeRange() == TimeRange.FromThisYear)) {
                            IndicatorCalcReqPropertyDto indicatorCalcReq = prepareIndicatorCalcReq(mainSecId, benchmarkId, indexIdList, indicatorCalcTimeRangeDto,
                                    dateSecAllNavMap, riskOfFreeId, riskOfFreeValue, null, calcFrequency, calcFrequency, indicatorList, geoExtraIndicatorList, ifAnnualize, calcIndexRetIndicatorValue,
                                    needRet, needExtraRet, false);
                            indicatorCalcReq.setLeastRetNum(leastRetNum);

                            IndicatorCalcPropertyDto indicatorCalcPropertyDto = CalcService.getInstance().indicatorCalc(indicatorCalcReq);

                            indicatorCalcPropertyDto.getIndicatorCalcReq().setAllNavMap(null);
                            indicatorCalcPropertyDto.setRiskFreeData(null);
                            indicatorCalcPropertyDto.getSecData().setOriNavList(null);
                            indicatorCalcPropertyDto.getSecData().setExtraNavList(null);
                            indicatorCalcPropertyDto.getSecData().setRetList(null);
                            indicatorCalcPropertyDto.getSecData().setExtraRetGeoList(null);
                            indicatorCalcPropertyDto.getBenchmarkData().setBenchmarkNavList(null);
                            indicatorCalcPropertyDto.getBenchmarkData().setOriBenchmarkNavList(null);
                            indicatorCalcPropertyDto.getBenchmarkData().setBenchmarkRetList(null);

                            indicatorCalcPropertyDtoList.add(indicatorCalcPropertyDto);
                        }
                    }
                }

                indicatorCalcPropertyDtoListMap.put(dataFrequency, indicatorCalcPropertyDtoList);
            }
            indicatorCalcPropertyDtoListMapMap.put(mainSecId, indicatorCalcPropertyDtoListMap);
        }

        return indicatorCalcPropertyDtoListMapMap;
    }

    public List<IndicatorCalcTimeRangeDto> getSecTimeRange(String mainSecId, Frequency frequency, Map<String, List<DateValue>> allNavMap) {
        List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = CollectionUtil.newArrayList();

        DateIntervalDto dateIntervalDto = new DateIntervalDto();
        dateIntervalDto.setDateIntervalType(DateIntervalType.DefaultInterval);

        List<DateValue> validNavList = allNavMap.get(mainSecId);

        if (CollectionUtil.isEmpty(validNavList)) {
            return indicatorCalcTimeRangeDtoList;
        }

        for (DateValue dateValue : validNavList) {
            dateValue.setIsvalid(1);
        }

        List<DateValue> mainSecIdNavList = allNavMap.get(mainSecId);

        String theFirstNavDate = "";
        String theLastNavDate = "";
        if (mainSecIdNavList != null && mainSecIdNavList.size() > 0) {
            theFirstNavDate = mainSecIdNavList.get(0).getDate();
            theLastNavDate = mainSecIdNavList.get(mainSecIdNavList.size() - 1).getDate();
        }

        List<IndexesTradeDateDo> indexesTradeDateDoList = TradeDateUtil.listInfoByDateInterval(theFirstNavDate, theLastNavDate);

        indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(dateIntervalDto, allNavMap.get(mainSecId), indexesTradeDateDoList
                , dateIntervalDto.getId());

        for (IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto : indicatorCalcTimeRangeDtoList) {
            if (frequency == Frequency.Monthly) {
                List<String> thisMonthAfter5Days = TradeDateUtil.getThisMonthAfter5Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
                indicatorCalcTimeRangeDto.setMonthPreDays(thisMonthAfter5Days);
                List<String> thisMonthBefore6Days = TradeDateUtil.getThisMonthBefore6Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
                indicatorCalcTimeRangeDto.setMonthLaterDays(thisMonthBefore6Days);
            } else {
                List<String> preFourTradeDays = TradeDateUtil.getBefore4TradeDate(indicatorCalcTimeRangeDto.getStartDate());
                indicatorCalcTimeRangeDto.setPreDays(preFourTradeDays);
            }
            List<DateValue> secIdValidNav = IndicatorCalcUtil.getSecIdValidNav(mainSecId, indicatorCalcTimeRangeDto, allNavMap, false, frequency);

            if (secIdValidNav.size() == 0) {
                indicatorCalcTimeRangeDto.setStartDate(null);
            }
        }

        return indicatorCalcTimeRangeDtoList;
    }

//
//    public Map<Integer, Map<String, String>> calcMultipleSecMultipleTimeRangeIndicator(List<String> mainSecIdList, List<Integer> idList, Map<String, String> idBenchmarkMap, List<String> indexIdList,
//                                                                                       Map<String, DateIntervalDto> idDateIntervalDtoMap, Map<String, Frequency> idNavFrequencyMap,
//                                                                                       Map<String, Frequency> idCalcFrequencyMap, Map<String, NavType> idNavTypeMap,
//                                                                                       Map<String, List<Indicator>> idIndicatorListMap, Map<String, List<Indicator>> idGeoExtraindicatorListMap,
//                                                                                       Map<String, String> idRiskOfFreeIdMap, Map<String, Double> idRiskOfFreeValueMap, Map<String, Benchmark> benchmarkMap) {
//
//        List<String> benchmarkIdList = new ArrayList<>();
//        for (Map.Entry<String, String> secBenchmarkIdMapEntry : idBenchmarkMap.entrySet()) {
//            String benchmarkId = secBenchmarkIdMapEntry.getValue();
//            if (BenchmarkConfig.rz_strategy.name().equals(benchmarkId)) {
//                for (Map.Entry<String, Benchmark> benchmarkMapEntry : benchmarkMap.entrySet()) {
//                    String bk = benchmarkMapEntry.getValue().getBenchmarkId();
//                    if (StringUtils.isNotEmpty(bk) && !benchmarkIdList.contains(bk)) {
//                        benchmarkIdList.add(bk);
//                    }
//                }
//            } else if (BenchmarkConfig.rz_bfi.name().equals(benchmarkId)) {
//                for (Map.Entry<String, Benchmark> benchmarkMapEntry : benchmarkMap.entrySet()) {
//                    String bk = benchmarkMapEntry.getValue().getRZIndexId();
//                    if (StringUtils.isNotEmpty(bk) && !benchmarkIdList.contains(bk)) {
//                        benchmarkIdList.add(bk);
//                    }
//                }
//            } else {
//                if (StringUtils.isNotEmpty(benchmarkId) && !benchmarkIdList.contains(benchmarkId)) {
//                    benchmarkIdList.add(benchmarkId);
//                }
//            }
//        }
//
//        List<NavType> navTypeList = new ArrayList<>();
//        Map<NavType, List<String>> navTypeIdListMap = new HashMap<>();
//        for (Map.Entry<String, NavType> idNavTypeMapEntry : idNavTypeMap.entrySet()) {
//            NavType value = idNavTypeMapEntry.getValue();
//            if (!navTypeIdListMap.containsKey(value)) {
//                List<String> list = new ArrayList<>();
//                list.add(idNavTypeMapEntry.getKey());
//                navTypeIdListMap.put(value, list);
//            } else {
//                navTypeIdListMap.get(value).add(idNavTypeMapEntry.getKey());
//            }
//        }
//
//        List<String> riskOfFreeIdList = new ArrayList<>();
//        for (Map.Entry<String, String> idRiskOfFreeIdMapEntry : idRiskOfFreeIdMap.entrySet()) {
//            String value = idRiskOfFreeIdMapEntry.getValue();
//            if (StringUtils.isNotEmpty(value) && !riskOfFreeIdList.contains(value)) {
//                riskOfFreeIdList.add(value);
//            }
//        }
//        benchmarkIdList.addAll(riskOfFreeIdList);
//
//        Map<NavType, Map<String, List<DateValue>>> navTypeAllNavMapMap = new HashMap<>();
//        for (Map.Entry<NavType, List<String>> navTypeIdListMapEntry : navTypeIdListMap.entrySet()) {
//            Map<String, List<DateValue>> allNavList = navService.getSecIdDateValueNavListMapFromRedisAndDB(navTypeIdListMapEntry.getValue(), benchmarkIdList, indexIdList,
//                    null, null, NavType.CumulativeNav, Visibility.Both, 1, 0, new HashMap<>(), false, null);
//            navTypeAllNavMapMap.put(navTypeIdListMapEntry.getKey(), allNavList);
//        }
//
//        Map<String, Frequency> secDefaultFrequency = fundInformationDao.getNavFrequency(mainSecIdList);
//
//        Map<String, Map<Frequency, List<DateValue>>> secFreValidMainSecNavListMapMap = new HashMap<>();
//
//        Map<Integer, Map<String, String>> result = new HashMap<>();
//        for (Integer id : idList) {
//            String idStr = id + "";
//            String benchmarkId = idBenchmarkMap.get(idStr);
//            NavType navType = idNavTypeMap.get(idStr);
//            Map<String, List<DateValue>> allNavMap = navTypeAllNavMapMap.get(navType);
//            String riskOfFreeId = idRiskOfFreeIdMap.get(idStr);
//
//            Double riskOfFreeValue = idRiskOfFreeValueMap.get(idStr);
//
//            Frequency navFrequency = idNavFrequencyMap.get(idStr);
//            Frequency calcFrequency = idCalcFrequencyMap.get(idStr);
//
//            List<Indicator> indicatorList = idIndicatorListMap.get(idStr);
//            List<Indicator> geoExtraindicatorList = idGeoExtraindicatorListMap.get(idStr);
//
//            Map<String, String> secIdValueMap = new HashMap<>();
//            for (String mainSecId : mainSecIdList) {
//                if (StringUtils.isNotEmpty(benchmarkId)) {
//                    Benchmark benchmark = benchmarkMap.get(mainSecId);
//                    if (BenchmarkConfig.rz_strategy.name().equals(benchmarkId)) {
//                        if (benchmark != null) {
//                            benchmarkId = benchmark.getBenchmarkId();
//                        }
//                    } else if (BenchmarkConfig.rz_bfi.name().equals(benchmarkId)) {
//                        if (benchmark != null) {
//                            benchmarkId = benchmark.getRZIndexId();
//                        }
//                    }
//                }
//
//                Map<String, List<DateValue>> secAllNavMap = new HashMap();
//                secAllNavMap.put(mainSecId, NavUtil.getNewNavList(allNavMap.get(mainSecId)));
//                secAllNavMap.put(benchmarkId, NavUtil.getNewNavList(allNavMap.get(benchmarkId)));
//                for (String indexId : indexIdList) {
//                    secAllNavMap.put(indexId, NavUtil.getNewNavList(allNavMap.get(indexId)));
//                }
//                if (StringUtils.isNotEmpty(riskOfFreeId)) {
//                    secAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(allNavMap.get(riskOfFreeId)));
//                }
//
//                List<IndicatorCalcPropertyDto> indicatorCalcPropertyDtoList = new ArrayList<>();
//
//                List<DateValue> mainSecIdNavList = secAllNavMap.get(mainSecId);
//
//                String theFirstNavDate = "";
//                String theLastNavDate = "";
//                if (mainSecIdNavList != null && mainSecIdNavList.size() > 0) {
//                    theFirstNavDate = mainSecIdNavList.get(0).getDate();
//                    theLastNavDate = mainSecIdNavList.get(mainSecIdNavList.size() - 1).getDate();
//                }
//
//                List<IndexesTradeDateDo> indexesTradeDateDoList = TradeDateUtil.listInfoByDateInterval(theFirstNavDate, theLastNavDate);
//
//                List<DateValue> validMainSecNavList = null;
//                if (secFreValidMainSecNavListMapMap.containsKey(mainSecId)) {
//                    Map<Frequency, List<DateValue>> frequencyListMap = secFreValidMainSecNavListMapMap.get(mainSecId);
//                    if (frequencyListMap.containsKey(navFrequency)) {
//                        validMainSecNavList = frequencyListMap.get(navFrequency);
//                        secAllNavMap.put(mainSecId, NavUtil.getNewNavList(validMainSecNavList));
//                    } else {
//                        validMainSecNavList = NavUtil.filterFrequencyNav(mainSecIdNavList, indexesTradeDateDoList, navFrequency);
//                        secAllNavMap.put(mainSecId, NavUtil.getNewNavList(validMainSecNavList));
//                        frequencyListMap.put(navFrequency, validMainSecNavList);
//                    }
//                } else {
//                    validMainSecNavList = NavUtil.filterFrequencyNav(mainSecIdNavList, indexesTradeDateDoList, navFrequency);
//                    secAllNavMap.put(mainSecId, NavUtil.getNewNavList(validMainSecNavList));
//                    Map<Frequency, List<DateValue>> freValidMainSecNavListMap = new HashMap<>();
//                    freValidMainSecNavListMap.put(navFrequency, validMainSecNavList);
//                    secFreValidMainSecNavListMapMap.put(mainSecId, freValidMainSecNavListMap);
//                }
//
//                Frequency defaultFrequency = secDefaultFrequency.get(mainSecId);
//
//                DateIntervalDto dateIntervalDto = idDateIntervalDtoMap.get(idStr);
//
//                List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(dateIntervalDto, secAllNavMap.get(mainSecId), indexesTradeDateDoList
//                        , dateIntervalDto.getId());
//                for (IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto : indicatorCalcTimeRangeDtoList) {
//
//                    Map<String, List<DateValue>> dateSecAllNavMap = new HashMap();
//                    dateSecAllNavMap.put(mainSecId, NavUtil.getNewNavList(secAllNavMap.get(mainSecId)));
//                    dateSecAllNavMap.put(benchmarkId, NavUtil.getNewNavList(secAllNavMap.get(benchmarkId)));
//                    for (String indexId : indexIdList) {
//                        dateSecAllNavMap.put(indexId, NavUtil.getNewNavList(secAllNavMap.get(indexId)));
//                    }
//                    if (StringUtils.isNotEmpty(riskOfFreeId)) {
//                        dateSecAllNavMap.put(riskOfFreeId, NavUtil.getNewNavList(secAllNavMap.get(riskOfFreeId)));
//                    }
//
//                    IndicatorCalcReqPropertyDto indicatorCalcReq = prepareIndicatorCalcReq(mainSecId, benchmarkId, indexIdList, indicatorCalcTimeRangeDto,
//                            dateSecAllNavMap, riskOfFreeId, riskOfFreeValue, null, calcFrequency, defaultFrequency, indicatorList, geoExtraindicatorList, true, true,
//                            false, false, true);
//
//                    IndicatorCalcPropertyDto indicatorCalcPropertyDto = calcService.indicatorCalc(indicatorCalcReq);
//
//                    indicatorCalcPropertyDtoList.add(indicatorCalcPropertyDto);
//                }
//
//                String value = null;
//                if (indicatorCalcPropertyDtoList != null && indicatorCalcPropertyDtoList.size() > 0) {
//                    IndicatorCalcPropertyDto indicatorCalcPropertyDto = indicatorCalcPropertyDtoList.get(0);
//                    if (indicatorList.size() > 0) {
//                        value = indicatorCalcPropertyDto.getSecData().getIndicatorValueMap().get(indicatorList.get(0).name());
//                    } else {
//                        value = indicatorCalcPropertyDto.getSecData().getExtraGeoIndicatorValueMap().get(geoExtraindicatorList.get(0).name());
//                    }
//                }
//                secIdValueMap.put(mainSecId, value);
//            }
//
//            result.put(id, secIdValueMap);
//        }
//
//        return result;
//    }

    private IndicatorCalcReqPropertyDto prepareTrendReq(String mainSecId, String benchmarkId, List<String> indexList, IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto,
                                                        Map<String, List<DateValue>> allNavMap, Frequency frequency, Frequency defaultFrequency,
                                                        List<TrendType> trendTypeV2List, BigDecimal fixedIncome, BigDecimal initValue) {

        IndicatorCalcReqPropertyDto indicatorCalcReq = new IndicatorCalcReqPropertyDto();
        indicatorCalcReq.setSecId(mainSecId);
        indicatorCalcReq.setBenchmarkId(benchmarkId);
        indicatorCalcReq.setIndexIdList(indexList);
        indicatorCalcReq.setAllNavMap(allNavMap);
        indicatorCalcReq.setTrendTypeList(trendTypeV2List);
        indicatorCalcReq.setFixedIncome(fixedIncome);
        indicatorCalcReq.setInitValue(initValue);
        indicatorCalcReq.setDefaultFrequency(defaultFrequency);

        if (defaultFrequency == Frequency.Monthly) {
            List<String> thisMonthAfter5Days = TradeDateUtil.getThisMonthAfter5Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
            indicatorCalcTimeRangeDto.setMonthPreDays(thisMonthAfter5Days);
            List<String> thisMonthBefore6Days = TradeDateUtil.getThisMonthBefore6Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
            indicatorCalcTimeRangeDto.setMonthLaterDays(thisMonthBefore6Days);
        } else {
            List<String> preFourTradeDays = TradeDateUtil.getBefore4TradeDate(indicatorCalcTimeRangeDto.getStartDate());
            indicatorCalcTimeRangeDto.setPreDays(preFourTradeDays);
        }

        indicatorCalcReq.setIndicatorCalcTimeRangeDto(indicatorCalcTimeRangeDto);
        return indicatorCalcReq;
    }

    private IndicatorCalcReqPropertyDto prepareIndicatorCalcReq(String mainSecId, String benchmarkId, List<String> indexList,
                                                                IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto,
                                                                Map<String, List<DateValue>> allNavMap,
                                                                String riskOfFreeId, Double riskOfFreeValue, Map<String, CompoundRet> stgDateRetMap, Frequency frequency, Frequency defaultFrequency,
                                                                List<Indicator> indicatorList, List<Indicator> geoExtraindicatorList, boolean ifAnnualize, boolean calcIndexRetIndicatorValue,
                                                                boolean needRet, boolean needExtraRet, boolean ifConvertPerformanceConsistencyWord) {
        IndicatorCalcReqPropertyDto indicatorCalcReq = new IndicatorCalcReqPropertyDto();
        indicatorCalcReq.setSecId(mainSecId);
        if (indexList != null) {
            indicatorCalcReq.setIndexIdList(indexList);
        }
        indicatorCalcReq.setBenchmarkId(benchmarkId);
        indicatorCalcReq.setAllNavMap(allNavMap);

        if (defaultFrequency == Frequency.Monthly) {
            List<String> thisMonthAfter5Days = TradeDateUtil.getThisMonthAfter5Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
            indicatorCalcTimeRangeDto.setMonthPreDays(thisMonthAfter5Days);
            List<String> thisMonthBefore6Days = TradeDateUtil.getThisMonthBefore6Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
            indicatorCalcTimeRangeDto.setMonthLaterDays(thisMonthBefore6Days);
        } else {
            List<String> preFourTradeDays = TradeDateUtil.getBefore4TradeDate(indicatorCalcTimeRangeDto.getStartDate());
            indicatorCalcTimeRangeDto.setPreDays(preFourTradeDays);
        }

        indicatorCalcReq.setIndicatorCalcTimeRangeDto(indicatorCalcTimeRangeDto);

        indicatorCalcReq.setStrategyDateRetMap(stgDateRetMap);
        indicatorCalcReq.setRiskOfFreeId(riskOfFreeId);
        indicatorCalcReq.setRiskOfFreeValue(riskOfFreeValue);
        indicatorCalcReq.setIndicatorList(indicatorList);
        indicatorCalcReq.setGeoIndicatorList(geoExtraindicatorList);

        indicatorCalcReq.setFrequency(frequency);
        indicatorCalcReq.setDefaultFrequency(defaultFrequency);

        indicatorCalcReq.setIfAnnualize(ifAnnualize);

        indicatorCalcReq.setNeedRet(needRet);

        indicatorCalcReq.setNeedExtraGeoRet(needExtraRet);

        indicatorCalcReq.setCalcIndexRetIndicatorValue(calcIndexRetIndicatorValue);

        indicatorCalcReq.setIfConvertPerformanceConsistencyWord(ifConvertPerformanceConsistencyWord);

        return indicatorCalcReq;
    }

    public void rollFindStartDate(String mainSecId, DateIntervalDto dateIntervalDto, List<IndexesTradeDateDo> indexesTradeDateDoList, Map<String, List<DateValue>> allNavMap, Frequency defaultFrequency) {
        if (dateIntervalDto.getDateIntervalType() == DateIntervalType.DefaultRolling || dateIntervalDto.getDateIntervalType() == DateIntervalType.CustomRolling) {
            DateIntervalDto defaultIntervalDateIntervalDto = new DateIntervalDto();
            defaultIntervalDateIntervalDto.setStartDate(dateIntervalDto.getStartDate());
            defaultIntervalDateIntervalDto.setEndDate(dateIntervalDto.getEndDate());
            defaultIntervalDateIntervalDto.setDateIntervalType(DateIntervalType.CustomInterval);
            defaultIntervalDateIntervalDto.setTimeRange(dateIntervalDto.getTimeRange());

            List<DateValue> newNavList = NavUtil.getNewNavList(allNavMap.get(mainSecId));
            List<IndicatorCalcTimeRangeDto> indicatorCalcTimeRangeDtoList = DateIntervalUtil.getIndicatorCalcTimeRangeDto(defaultIntervalDateIntervalDto, newNavList, indexesTradeDateDoList
                    , dateIntervalDto.getId());

            if (indicatorCalcTimeRangeDtoList != null && indicatorCalcTimeRangeDtoList.size() > 0) {
                IndicatorCalcTimeRangeDto indicatorCalcTimeRangeDto = indicatorCalcTimeRangeDtoList.get(0);
                if (defaultFrequency == Frequency.Monthly) {
                    List<String> thisMonthAfter5Days = TradeDateUtil.getThisMonthAfter5Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
                    indicatorCalcTimeRangeDto.setMonthPreDays(thisMonthAfter5Days);
                    List<String> thisMonthBefore6Days = TradeDateUtil.getThisMonthBefore6Days(indicatorCalcTimeRangeDto.getStartDate(), indicatorCalcTimeRangeDto.getEndDate());
                    indicatorCalcTimeRangeDto.setMonthLaterDays(thisMonthBefore6Days);
                } else {
                    List<String> preFourTradeDays = TradeDateUtil.getBefore4TradeDate(indicatorCalcTimeRangeDto.getStartDate());
                    indicatorCalcTimeRangeDto.setPreDays(preFourTradeDays);
                }
                Map<String, List<DateValue>> defaultIntervalNavMap = new HashMap<>();
                defaultIntervalNavMap.put(mainSecId, newNavList);
                List<DateValue> secIdValidNav = IndicatorCalcUtil.getSecIdValidNav(mainSecId, indicatorCalcTimeRangeDto, defaultIntervalNavMap, false, defaultFrequency);
                if (secIdValidNav != null && secIdValidNav.size() > 0) {
                    dateIntervalDto.setStartDate(secIdValidNav.get(0).getDate());
                }
            }
        }
    }

    public List<Map<String, Object>> getMultiSecRetList(List<String> mainSecIdList, List<String> indexIds, Map<String, List<DateValue>> allNavMap) {
        List<Map<String, Object>> initRetList = ListUtil.list(true);
        for (String secId : mainSecIdList) {
            List<CompoundRet> ret = IndicatorCalcUtil.getRet(allNavMap.get(secId), null, null, null);
            List<Map<String, Object>> dataList = ret.stream().filter(e -> e.getRetOfFund() != null).map(e -> MapUtil.<String, Object>builder("end_date", e.getEndDate())
                    .put("fund_id", secId).put("ret_range", e.getRetOfFund()).build()).collect(Collectors.toList());
            initRetList.addAll(dataList);
        }
        for (String indexId : indexIds) {
            List<CompoundRet> indexRet = IndicatorCalcUtil.getRet(allNavMap.get(indexId), null, null, null);
            initRetList.addAll(indexRet.stream().filter(e -> e.getRetOfFund() != null).map(e -> MapUtil.<String, Object>builder("end_date", e.getEndDate())
                    .put("fund_id", indexId).put("ret_range", e.getRetOfFund()).build()).collect(Collectors.toList()));
        }
        return initRetList;
    }

    public Map<String, List<IndicatorCalcPropertyDto>> getMultiSecRetListNew(List<String> mainSecIdList, List<String> indexIdList, Frequency frequency,
                                                                             Frequency rollingFrequency, String startDate, String endDate,
                                                                             boolean ifExcessReturn, String benchmarkId, RaiseType raiseType, IStrategy strategy,
                                                                             Visibility visibility, NavType navType, boolean isExtract, Integer userId, Map<String, List<DateValue>> allNavMap) {
        List<DateIntervalDto> dateIntervalDtoList = new ArrayList<>();
        DateIntervalDto defaultIntervalDto = new DateIntervalDto();
        defaultIntervalDto.setStartDate(startDate);
        defaultIntervalDto.setEndDate(endDate);
        defaultIntervalDto.setDateIntervalType(DateIntervalType.DefaultRolling);
        defaultIntervalDto.setFrequency(rollingFrequency);
        dateIntervalDtoList.add(defaultIntervalDto);

        List<Indicator> indicatorList = new ArrayList<>();
        List<Indicator> geoExtraIndicatorList = new ArrayList<>();

        indicatorList.add(Indicator.IntervalReturn);
        if (ifExcessReturn) {
            geoExtraIndicatorList.add(Indicator.IntervalReturn);
        }

        Map<String, String> secBenchmarkIdMap = new HashMap<>();
        for (String mainSecId : mainSecIdList) {
            secBenchmarkIdMap.put(mainSecId, benchmarkId);
        }

        Map<String, List<DateIntervalDto>> secDateIntervalDtoListMap = new HashMap<>();
        for (String mainSecId : mainSecIdList) {
            secDateIntervalDtoListMap.put(mainSecId, dateIntervalDtoList);
        }

        CalcMultipleSecMultipleTimeRangeIndicatorReq calcMultipleSecMultipleTimeRangeIndicatorReq = new CalcMultipleSecMultipleTimeRangeIndicatorReq();
        calcMultipleSecMultipleTimeRangeIndicatorReq.setMainSecIdList(mainSecIdList);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setSecBenchmarkIdMap(secBenchmarkIdMap);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setIndexIdList(indexIdList);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setRaiseType(raiseType);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setStrategy(strategy);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setVisibility(visibility);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setSecDateIntervalDtoListMap(secDateIntervalDtoListMap);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setDataFrequency(frequency);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setNavType(navType);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setIndicatorList(indicatorList);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setGeoExtraindicatorList(geoExtraIndicatorList);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setRiskOfFreeId(null);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setRiskOfFreeValue(null);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setIfAnnualize(true);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setCalcIndexRetIndicatorValue(true);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setNeedRet(false);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setIfExtract(isExtract);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setUserId(userId);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setNeedExtraRet(false);
        calcMultipleSecMultipleTimeRangeIndicatorReq.setIfConvertPerformanceConsistencyWord(true);

        return calcMultipleSecMultipleTimeRangeIndicator(calcMultipleSecMultipleTimeRangeIndicatorReq, allNavMap);
    }

    private static class IndicatorServiceBuilder {
        public static final IndicatorService INSTANCE = new IndicatorService();
    }
}

