package com.simuwang.manage.task;

import cn.hutool.core.date.DateUtil;
import com.simuwang.base.common.enums.DeletionType;
import com.simuwang.base.common.enums.DistributeType;
import com.simuwang.base.common.enums.Frequency;
import com.simuwang.base.common.util.DateUtils;
import com.simuwang.base.common.util.StringUtil;
import com.simuwang.base.mapper.*;
import com.simuwang.base.pojo.dos.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

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

/**
 * FileName: FundDeletionTask
 * Author:   chenjianhua
 * Date:     2024/9/18 22:43
 * Description: ${DESCRIPTION}
 */
@EnableScheduling
@Component
public class FundDeletionTask {

    @Autowired
    private NavMapper navMapper;

    @Autowired
    private AssetMapper assetMapper;

    @Autowired
    private FundInfoMapper fundInfoMapper;

    @Autowired
    private FundReportFrequencyMapper fundReportFrequencyMapper;

    @Autowired
    private TradeDateMapper tradeDateMapper;

    @Autowired
    private DeletionInfoMapper deletionInfoMapper;

    @Autowired
    private DistributionMapper distributionMapper;
    @Scheduled(cron = "0 0/5 * * * ?")
    public void computeDeletion(){
        List<String> fundIdList = navMapper.getAllFundId();
        for(String fundId : fundIdList){
            String inceptionDate = fundInfoMapper.getInceptionDateByFundId(fundId);
            if(inceptionDate == null){
                continue;
            }
            String today = DateUtils.getAroundToday(0);
            //获取基金对应的净值报送频率
            FundReportFrequencyDO fundReportFrequencyDO = fundReportFrequencyMapper.getFrequencyByFundId(fundId);
            if(StringUtil.isNull(fundReportFrequencyDO)){
                continue;
            }
            List<NavDO> navDOList = navMapper.selectNavByFundId(fundId);
            List<AssetDO> assetDOList = assetMapper.selectAssetByFundId(fundId);
            //查询成立日到今天为止的交易日集合
            List<TradeDateDO> tradeDateDOList = tradeDateMapper.selectTradeDate(inceptionDate,today);
            navDeletion(fundId,navDOList,tradeDateDOList,fundReportFrequencyDO);
            assetDeletion(fundId,assetDOList,tradeDateDOList,fundReportFrequencyDO);
            distributionDeletion(fundId,navDOList);
        }
    }

    private void distributionDeletion(String fundId, List<NavDO> navDOList) {
        if(navDOList.size() < 1){
            return;
        }
        //查询是否存在拆分
        List<DistributionDO> distributionDOS = distributionMapper.getDistributionByFundId(fundId, DistributeType.DIVIDENDS_SPLIT);
        if(distributionDOS.size() > 0){
            //存在拆分，不做分红缺失计算，同时把以往的数据添加备注
            deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),null,DeletionType.EXIST_SPLIT.getInfo());
            return;
        }
        BigDecimal threshold = new BigDecimal(0.0035);
        BigDecimal preDifference = new BigDecimal(0);
        for(int navIdx=0;navIdx < navDOList.size() ;navIdx++){
            NavDO navDO = navDOList.get(navIdx);
            //获取当前净值日期下的分红总和
            BigDecimal sumDistribute = distributionMapper.getSumDistributeByFundId(navDO.getFundId(),DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD));
            BigDecimal nav = navDO.getNav();
            BigDecimal cumulativeNavWithdrawal  = navDO.getCumulativeNavWithdrawal();
            if(sumDistribute == null){
                sumDistribute = new BigDecimal(0);
            }
            //不存在分红的时候,判断当前的差值是否符合要求
            BigDecimal difference = cumulativeNavWithdrawal.subtract(nav.add(sumDistribute));
            if(difference.compareTo(threshold) > 0){
                //存在缺失
                String tradeDate = null;
                if(navIdx == 0 || navDOList.size() ==1){
                    //有且仅有一条数据
                    tradeDate = DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
                }else{
                    //判断前一条的差值与当前是否一致，一致就不处理
                    if(difference.compareTo(preDifference) == 0){
                        continue;
                    }
                    tradeDate = DateUtils.format(navDOList.get(navIdx-1).getPriceDate(),DateUtils.YYYY_MM_DD)+"~"+DateUtils.format(navDO.getPriceDate(),DateUtils.YYYY_MM_DD);
                }
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.DISTRIBUTION_DELETION.getCode());
            }
            preDifference = difference;
        }
    }

    private void assetDeletion(String fundId, List<AssetDO> assetDOList, List<TradeDateDO> tradeDateDOList, FundReportFrequencyDO fundReportFrequencyDO) {
        if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
            Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
            for(String tradeDate : tradeListMap.keySet()){
                if(navListMap.containsKey(tradeDate)){
                    deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
            }
        }
        if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
            Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            TreeMap<Integer,List<AssetDO>> weekNavListMap = new TreeMap<>();
            //按周数整合
            for(String priceDate : navListMap.keySet()){
                Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
                if(weekNavListMap.containsKey(weekOfYear)){
                    List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
                    assetDOS.addAll(navListMap.get(priceDate));
                    weekNavListMap.put(weekOfYear,assetDOS);
                }else{
                    List<AssetDO> navDOS = new ArrayList<>();
                    navDOS.addAll(navListMap.get(priceDate));
                    weekNavListMap.put(weekOfYear,navDOS);
                }
            }
            Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
            for(Integer weekOfYear : tradeListMap.keySet()){
                if(weekNavListMap.containsKey(weekOfYear)){
                    List<AssetDO> assetDOS = weekNavListMap.get(weekOfYear);
                    String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
                    deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //不包含的话，默认取每周的最后一个交易日作为周净值日期
                List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
                String tradeDate = null;
                if(tradeDateDOS.size() > 0){
                    tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
            }
        }
        if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getAssetFrequency())){
            Map<String,List<AssetDO>> navListMap = assetDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            TreeMap<String,List<AssetDO>> monthNavListMap = new TreeMap<>();
            //按周数整合
            for(String priceDate : navListMap.keySet()){
                String yearMonth = priceDate.substring(0,7);
                if(monthNavListMap.containsKey(yearMonth)){
                    List<AssetDO> navDOS = monthNavListMap.get(yearMonth);
                    navDOS.addAll(navListMap.get(priceDate));
                    monthNavListMap.put(yearMonth,navDOS);
                }else{
                    List<AssetDO> navDOS = new ArrayList<>();
                    navDOS.addAll(navListMap.get(priceDate));
                    monthNavListMap.put(yearMonth,navDOS);
                }
            }
            Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
            for(String yearMonth : tradeListMap.keySet()){
                if(monthNavListMap.containsKey(yearMonth)){
                    List<AssetDO> assetDOS = monthNavListMap.get(yearMonth);
                    String tradeDate = DateUtils.format(assetDOS.get(assetDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
                    deletionInfoMapper.updateRemark(fundId,DeletionType.ASSET_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //不包含的话，默认取每周的最后一个交易日作为周净值日期
                List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
                String tradeDate = null;
                if(tradeDateDOS.size() > 0){
                    tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.ASSET_DELETION.getCode());
            }
        }
    }

    private void navDeletion(String fundId,List<NavDO> navDOList, List<TradeDateDO> tradeDateDOList,FundReportFrequencyDO fundReportFrequencyDO) {
        //只处理日月季频率
        if(Frequency.DAY == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
            Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getTradeDate(),DateUtils.YYYY_MM_DD)));
            for(String tradeDate : tradeListMap.keySet()){
                if(navListMap.containsKey(tradeDate)){
                    deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
            }
        }
        if(Frequency.WEEK == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
            Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            TreeMap<Integer,List<NavDO>> weekNavListMap = new TreeMap<>();
            //按周数整合
            for(String priceDate : navListMap.keySet()){
                Integer weekOfYear = DateUtil.weekOfYear(DateUtils.parse(priceDate,DateUtils.YYYY_MM_DD));
                if(weekNavListMap.containsKey(weekOfYear)){
                    List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
                    navDOS.addAll(navListMap.get(priceDate));
                    weekNavListMap.put(weekOfYear,navDOS);
                }else{
                    List<NavDO> navDOS = new ArrayList<>();
                    navDOS.addAll(navListMap.get(priceDate));
                    weekNavListMap.put(weekOfYear,navDOS);
                }
            }
            Map<Integer,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getWeekOfYear()));
            for(Integer weekOfYear : tradeListMap.keySet()){
                if(weekNavListMap.containsKey(weekOfYear)){
                    List<NavDO> navDOS = weekNavListMap.get(weekOfYear);
                    String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
                    deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //不包含的话，默认取每周的最后一个交易日作为周净值日期
                List<TradeDateDO> tradeDateDOS = tradeListMap.get(weekOfYear);
                String tradeDate = null;
                if(tradeDateDOS.size() > 0){
                    tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
            }
        }
        if(Frequency.MONTH == Frequency.getFrequencyByCode(fundReportFrequencyDO.getNavFrequency())){
            Map<String,List<NavDO>> navListMap = navDOList.stream().collect(Collectors.groupingBy(e -> DateUtils.format(e.getPriceDate(),DateUtils.YYYY_MM_DD)));
            TreeMap<String,List<NavDO>> monthNavListMap = new TreeMap<>();
            //按周数整合
            for(String priceDate : navListMap.keySet()){
                String yearMonth = priceDate.substring(0,7);
                if(monthNavListMap.containsKey(yearMonth)){
                    List<NavDO> navDOS = monthNavListMap.get(yearMonth);
                    navDOS.addAll(navListMap.get(priceDate));
                    monthNavListMap.put(yearMonth,navDOS);
                }else{
                    List<NavDO> navDOS = new ArrayList<>();
                    navDOS.addAll(navListMap.get(priceDate));
                    monthNavListMap.put(yearMonth,navDOS);
                }
            }
            Map<String,List<TradeDateDO>> tradeListMap = tradeDateDOList.stream().collect(Collectors.groupingBy(e -> e.getYearmonth()));
            for(String yearMonth : tradeListMap.keySet()){
                if(monthNavListMap.containsKey(yearMonth)){
                    List<NavDO> navDOS = monthNavListMap.get(yearMonth);
                    String tradeDate = DateUtils.format(navDOS.get(navDOS.size()-1).getPriceDate(),DateUtils.YYYY_MM_DD);
                    deletionInfoMapper.updateRemark(fundId,DeletionType.NAV_DELETION.getCode(),tradeDate,DeletionType.NO_DELETION.getInfo());
                    continue;
                }
                //不包含的话，默认取每周的最后一个交易日作为周净值日期
                List<TradeDateDO> tradeDateDOS = tradeListMap.get(yearMonth);
                String tradeDate = null;
                if(tradeDateDOS.size() > 0){
                    tradeDate = DateUtils.format(tradeDateDOS.get(tradeDateDOS.size()-1).getTradeDate(),DateUtils.YYYY_MM_DD);
                }
                //写入缺失信息表
                saveDeletionInfoDO(fundId,tradeDate,DeletionType.NAV_DELETION.getCode());
            }
        }
    }

    private void saveDeletionInfoDO(String fundId, String tradeDate, Integer code) {
        if(tradeDate == null){
            return;
        }
        DeletionInfoDO deletionInfoDO = new DeletionInfoDO();
        deletionInfoDO.setFundId(fundId);
        deletionInfoDO.setDeletionDate(tradeDate);
        deletionInfoDO.setDeletionType(code);
        DeletionInfoDO oldDeletionDO = deletionInfoMapper.getDeletionInfoDO(deletionInfoDO);
        if(StringUtil.isNull(oldDeletionDO)){
            deletionInfoDO.setIsvalid(1);
            deletionInfoDO.setUpdateTime(DateUtils.getNowDate());
            deletionInfoDO.setCreateTime(DateUtils.getNowDate());
            deletionInfoMapper.saveDeletionInfoDO(deletionInfoDO);
        }
    }
}
