package com.hz.pm.api.dashboard.manage;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.hz.pm.api.common.helper.RegionCacheHelper;
import com.hz.pm.api.common.model.constant.RegionConst;
import com.hz.pm.api.common.model.entity.DataDTO;
import com.hz.pm.api.dashboard.constant.DashboardConstant;
import com.hz.pm.api.dashboard.model.vo.ExpertStatisticsVO;
import com.hz.pm.api.expert.entity.ExpertIntentionWorkRegion;
import com.hz.pm.api.expert.entity.ExpertUserFullInfo;
import com.hz.pm.api.expert.service.IExpertIntentionWorkRegionService;
import com.hz.pm.api.expert.service.IExpertUserFullInfoService;
import com.hz.pm.api.meta.model.dto.DictionaryDTO;
import com.hz.pm.api.meta.model.dto.TagDTO;
import com.hz.pm.api.meta.model.entity.ExpertDictionary;
import com.hz.pm.api.meta.model.entity.ExpertTag;
import com.hz.pm.api.meta.service.IExpertDictionaryService;
import com.hz.pm.api.meta.service.IExpertTagService;
import com.hz.pm.api.meta.service.IMetaDictionaryService;
import com.hz.pm.api.meta.service.IMetaTagService;
import com.hz.pm.api.sys.model.dto.RegionDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;

/**
 * @Classname ExpertStatisticsManage
 * @Description
 * @Date 2023/12/04 17:44
 * @Author PoffyZhang
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class ExpertStatisticsManage {

    private final RegionCacheHelper regionCacheHelper;

    private final IExpertUserFullInfoService userFullInfoService;

    private final IExpertIntentionWorkRegionService expertIntentionWorkRegionService;

    private final IExpertTagService expertTagService;

    private final IMetaTagService metaTagService;

    private final IExpertDictionaryService expertDictionaryService;

    private final IMetaDictionaryService metaDictionaryService;

    private Integer[] years = new Integer[]{LocalDateTime.now().getYear() - 2,
            LocalDateTime.now().getYear() - 1, LocalDateTime.now().getYear(),
            LocalDateTime.now().getYear() + 1, LocalDateTime.now().getYear() + 2};

    private Integer[] threeYears = new Integer[]{LocalDateTime.now().getYear() - 2,
            LocalDateTime.now().getYear() - 1, LocalDateTime.now().getYear()};

    private String[] SHENGWEIDU = new String[]{DashboardConstant.Expert.NETWORK_CODE,
            DashboardConstant.Expert.DANGZHENG_CODE, DashboardConstant.Expert.FANGAN_CODE,
            DashboardConstant.Expert.JISHU_CODE, DashboardConstant.Expert.XINCHUANG_CODE,
            DashboardConstant.Expert.RUANYING_CODE};

    private String[] SHIWEIDU = new String[]{DashboardConstant.Expert.CAIWU_CODE,
            DashboardConstant.Expert.XINXIHUA_CODE, DashboardConstant.Expert.XINCHUANG_OTTHER_CODE};

    private String[] TYPES = new String[]{DashboardConstant.Expert.EXPERT_TYPE_INNER,
            DashboardConstant.Expert.EXPERT_TYPE_OUT};

    private String[] LEVELS = new String[]{DashboardConstant.Expert.EXPERT_LEVEL_ZHENGGAO,
            DashboardConstant.Expert.EXPERT_LEVEL_FUGAO, DashboardConstant.Expert.EXPERT_LEVEL_MID,
            DashboardConstant.Expert.EXPERT_LEVEL_CHUJI, DashboardConstant.Expert.EXPERT_LEVEL_NULL};

    /**
     * 专家统计
     *
     * @param year
     * @return
     */
    public ExpertStatisticsVO statistics(Integer year) {
        List<ExpertUserFullInfo> experts = userFullInfoService.list();
        List<ExpertIntentionWorkRegion> intentionWorkRegions = expertIntentionWorkRegionService.list();

        List<ExpertTag> expertTags = expertTagService.list();

        List<TagDTO> tags = metaTagService.queryAll();

        List<DictionaryDTO> dictionaries = metaDictionaryService.queryAll();
        Set<String> code = Sets.newHashSet();
        Map<String, String> dictionMap = dictionaries.stream().filter(d -> code.add(d.getDictionaryCode()))
                .collect(Collectors.toMap(DictionaryDTO::getDictionaryCode, DictionaryDTO::getName));

        List<ExpertDictionary> expertDictionaries = expertDictionaryService.list(Wrappers.lambdaQuery(ExpertDictionary.class)
                .eq(ExpertDictionary::getExpertInfoField, DashboardConstant.Expert.EXPERT_TYPE));

        ExpertStatisticsVO res = new ExpertStatisticsVO();
        CompletableFuture.allOf(
                CompletableFuture.runAsync(() -> {
                    //选择的年份去计算的数据
                    yearCompleteExpert(res, year, experts, intentionWorkRegions, expertTags,
                            tags, expertDictionaries, dictionMap);
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    //近三年
                    threeYearsCompleteExpert(res, experts);
                }, ForkJoinPool.commonPool()),
                CompletableFuture.runAsync(() -> {
                    //近一年
                    lastYearsCompleteExpert(res, experts);
                }, ForkJoinPool.commonPool())
        ).join();

        return res;
    }

    /**
     * 近一年
     *
     * @param res
     */
    private void lastYearsCompleteExpert(ExpertStatisticsVO res, List<ExpertUserFullInfo> experts) {
        List<ExpertUserFullInfo> lastYear = experts.stream().filter(expert -> {
            if (expert.getCreateOn().compareTo(LocalDateTime.now().plusYears(-1)) >= 0 &&
                    expert.getCreateOn().compareTo(LocalDateTime.now()) <= 0) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList());
        res.setThisYearAdded(lastYear.size());
    }

    /**
     * 近三年
     *
     * @param res
     */
    private void threeYearsCompleteExpert(ExpertStatisticsVO res, List<ExpertUserFullInfo> experts) {
        List<DataDTO> threeYearsAdds = Lists.newArrayList();
        for (Integer year : threeYears) {
            threeYearsAdds.add(DataDTO.of(year.toString(), year.toString(),
                    experts.stream().filter(e -> {
                        if (Objects.nonNull(e.getCreateOn()) && year.equals(e.getCreateOn().getYear())) {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    }).collect(Collectors.toList()).size()));
        }
        res.setThreeYearsAdded(threeYearsAdds);
    }

    /**
     * 选择年份或者全部
     *
     * @param res
     * @param year
     */
    private void yearCompleteExpert(ExpertStatisticsVO res, Integer year,
                                    List<ExpertUserFullInfo> experts, List<ExpertIntentionWorkRegion> intentionWorkRegions,
                                    List<ExpertTag> expertTags, List<TagDTO> tags, List<ExpertDictionary> expertDictionaries,
                                    Map<String, String> dictionMap) {
        res.setTotal(experts.size());

        Set<Long> userSet = Sets.newHashSet();
        Map<Long, ExpertUserFullInfo> expertMap = experts.stream().filter(e -> userSet.add(e.getUserId()))
                .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e));

        List<RegionDTO> regions = regionCacheHelper.listChildren(RegionConst.RC_HZ, RegionConst.RL_CITY)
                .stream().filter(r -> r.getRegionLevel().equals(RegionConst.RL_COUNTY))
                .sorted(Comparator.comparing(RegionDTO::getRegionCode)).collect(Collectors.toList());

        //是否要筛选年
        experts = experts.stream().filter(e -> {
            if (Objects.nonNull(e.getCreateOn()) &&
                    (Objects.isNull(year) || year.equals(e.getCreateOn().getYear()))) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }).collect(Collectors.toList());

        //计算区域专家
        computeRegionExperts(res, intentionWorkRegions, expertMap, regions);

        //计算专家特长
        computeRegionDistribution(res, experts, expertTags, tags);

        //计算专家类型
        computeTypes(res, experts, expertDictionaries, dictionMap);

        //计算专家等级
        computeLevels(res, experts, expertDictionaries, dictionMap);
    }

    /**
     * 计算 专家等级
     *
     * @param res
     * @param experts
     */
    private void computeLevels(ExpertStatisticsVO res, List<ExpertUserFullInfo> experts,
                               List<ExpertDictionary> expertDictionaries, Map<String, String> dictionMap) {
        List<DataDTO> levels = Lists.newArrayList();

        Set<Long> userSets = Sets.newHashSet();
        Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream()
                .filter(u -> userSets.add(u.getUserId()))
                .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e));

        Map<String, List<ExpertDictionary>> dictionaryMap = expertDictionaries.stream()
                .collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode));
        for (String levelCode : LEVELS) {
            int num = 0;
            if (dictionaryMap.containsKey(levelCode)) {
                List<ExpertDictionary> dictionaries = dictionaryMap.get(levelCode);
                num = dictionaries.stream().filter(d -> {
                    if (expertUserFullInfoMap.containsKey(d.getUserId())) {
                        return Boolean.TRUE;
                    }
                    return Boolean.FALSE;
                }).collect(Collectors.toList()).size();
            }
            levels.add(DataDTO.of(dictionMap.get(levelCode), levelCode, num));
        }

        res.setLevels(levels);
    }

    /**
     * 计算 专家类型
     *
     * @param res
     * @param experts
     */
    private void computeTypes(ExpertStatisticsVO res, List<ExpertUserFullInfo> experts,
                              List<ExpertDictionary> expertDictionaries, Map<String, String> dictionMap) {
        List<DataDTO> types = Lists.newArrayList();

        Set<Long> userSets = Sets.newHashSet();
        Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream()
                .filter(u -> userSets.add(u.getUserId()))
                .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e));

        Map<String, List<ExpertDictionary>> dictionaryMap = expertDictionaries.stream()
                .collect(Collectors.groupingBy(ExpertDictionary::getDictionaryCode));
        for (String typeCode : TYPES) {
            int num = 0;
            if (dictionaryMap.containsKey(typeCode)) {
                List<ExpertDictionary> dictionaries = dictionaryMap.get(typeCode);
                num = dictionaries.stream().filter(d -> {
                    if (expertUserFullInfoMap.containsKey(d.getUserId())) {
                        return Boolean.TRUE;
                    }
                    return Boolean.FALSE;
                }).collect(Collectors.toList()).size();
            }
            types.add(DataDTO.of(dictionMap.get(typeCode), typeCode, num));
        }

        res.setTypes(types);
    }

    //计算专家特长
    private void computeRegionDistribution(ExpertStatisticsVO res, List<ExpertUserFullInfo> experts,
                                           List<ExpertTag> expertTags, List<TagDTO> tags) {
        Set<Long> userSets = Sets.newHashSet();
        Map<Long, ExpertUserFullInfo> expertUserFullInfoMap = experts.stream()
                .filter(u -> userSets.add(u.getUserId()))
                .collect(Collectors.toMap(ExpertUserFullInfo::getUserId, e -> e));

        Map<String, List<DataDTO>> typeDistribution = Maps.newHashMap();

        Map<String, List<ExpertTag>> tagsMap =
                expertTags.stream().filter(e -> Objects.nonNull(e.getTagCode()))
                        .collect(Collectors.groupingBy(ExpertTag::getTagCode));

        Set<String> tagSets = Sets.newHashSet();
        Map<String, String> tagNameMap = tags.stream().filter(t -> tagSets.add(t.getTagCode()))
                .collect(Collectors.toMap(TagDTO::getTagCode, TagDTO::getTagName));

        //1.省维度
        List<DataDTO> shengWeidu = Lists.newArrayList();
        for (String code : SHENGWEIDU) {
            shengWeidu.add(getExpertData(expertUserFullInfoMap,
                    tagsMap, tagNameMap, code));
        }

        //2.市维度
        List<DataDTO> shiWeidu = Lists.newArrayList();
        for (String code : SHIWEIDU) {
            shiWeidu.add(getExpertData(expertUserFullInfoMap,
                    tagsMap, tagNameMap, code));
        }

        typeDistribution.put(DashboardConstant.Expert.DISTRIBUTION_SHENG, shengWeidu);
        typeDistribution.put(DashboardConstant.Expert.DISTRIBUTION_SHI, shiWeidu);
        res.setTypeDistribution(typeDistribution);
    }

    /**
     * 包装
     *
     * @param expertUserFullInfoMap
     * @param tagsMap
     * @param tagNameMap
     * @param code
     * @return
     */
    private static DataDTO getExpertData(Map<Long, ExpertUserFullInfo> expertUserFullInfoMap, Map<String, List<ExpertTag>> tagsMap, Map<String, String> tagNameMap, String code) {
        int num = 0;
        if (tagsMap.containsKey(code)) {
            List<ExpertTag> ets = tagsMap.get(code);
            num = ets.stream().filter(e -> {
                if (expertUserFullInfoMap.containsKey(e.getUserId())) {
                    return Boolean.TRUE;
                }
                return Boolean.FALSE;
            }).collect(Collectors.toList()).size();
        }
        return DataDTO.of(tagNameMap.get(code), code, num);
    }

    //计算区域专家
    private static void computeRegionExperts(ExpertStatisticsVO res, List<ExpertIntentionWorkRegion> intentionWorkRegions, Map<Long, ExpertUserFullInfo> expertMap, List<RegionDTO> regions) {
        List<DataDTO> regionExpert = Lists.newArrayList();
        for (RegionDTO region : regions) {
            regionExpert.add(DataDTO.of(region.getRegionName(), region.getRegionCode(),
                    intentionWorkRegions.stream().filter(w -> {
                        if (Objects.nonNull(w) && Objects.nonNull(w.getRegionCode()) &&
                                w.getRegionCode().equals(region.getRegionCode()) &&
                                expertMap.containsKey(w.getUserId())) {
                            return Boolean.TRUE;
                        }
                        return Boolean.FALSE;
                    }).collect(Collectors.toList()).size()));
        }
        res.setRegionExpert(regionExpert);
    }
}
