package com.hz.pm.api.meeting.helper;

import cn.hutool.core.util.RandomUtil;
import com.hz.pm.api.expert.entity.ExpertUserFullInfo;
import com.ningdatech.basic.util.CollUtils;
import com.hz.pm.api.expert.entity.ExpertGovBusinessStrip;
import com.hz.pm.api.meeting.entity.domain.MeetingExpert;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.RandomUtils;

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

/**
 * <p>
 * 专家抽取算法
 * </p>
 *
 * @author WendyYang
 * @since 2023/4/14
 **/
public class ExpertRandomInviteAlgorithm {

    private ExpertRandomInviteAlgorithm() {

    }

    private static boolean expertMatchNone(List<MeetingExpert> experts, ExpertUserFullInfo userInfo) {
        return experts.stream().noneMatch(w -> w.getExpertId().equals(userInfo.getUserId()));
    }

    /**
     * 每个单位只抽取一人
     *
     * @param expertGroupByUnit      需要抽取的人
     * @param expertsByRecentMeeting 最近会议抽取到的专家
     * @param count                  抽取数量
     * @return 抽取到的专家信息
     * @author WendyYang
     **/
    public static List<ExpertUserFullInfo> inviteGroupByUnit(Map<String, List<ExpertUserFullInfo>> expertGroupByUnit,
                                                             List<List<MeetingExpert>> expertsByRecentMeeting,
                                                             Integer count) {
        if (MapUtils.isEmpty(expertGroupByUnit)) {
            return Collections.emptyList();
        }
        if (expertsByRecentMeeting.isEmpty()) {
            return expertGroupByUnit.values().stream()
                    .map(RandomUtil::randomEle)
                    .limit(count).collect(Collectors.toList());
        } else {
            List<ExpertUserFullInfo> result = new ArrayList<>();
            List<String> keySet = new ArrayList<>(expertGroupByUnit.keySet());
            for (int i = 0; i < count; i++) {
                String company = keySet.get(RandomUtils.nextInt(0, keySet.size()));
                List<ExpertUserFullInfo> expertsByCompany = expertGroupByUnit.get(company);
                for (int j = 0; j < expertsByRecentMeeting.size(); j++) {
                    List<MeetingExpert> experts = expertsByRecentMeeting.get(j);
                    List<ExpertUserFullInfo> notInvitedUsers = expertsByCompany.stream()
                            .filter(w -> expertMatchNone(experts, w))
                            .collect(Collectors.toList());
                    if (!notInvitedUsers.isEmpty()) {
                        result.add(RandomUtil.randomEle(notInvitedUsers));
                        break;
                    } else if (j == (expertsByRecentMeeting.size() - 1)) {
                        result.add(RandomUtil.randomEle(expertsByCompany));
                    }
                }
                if (result.size() < count) {
                    keySet.remove(company);
                    if (keySet.isEmpty()) {
                        break;
                    }
                }
            }
            return result;
        }
    }

    public static List<ExpertUserFullInfo> inviteGroupByStrip(Map<String, List<ExpertGovBusinessStrip>> expertStripGroup,
                                                              List<ExpertUserFullInfo> userFullInfoList,
                                                              List<List<MeetingExpert>> expertsByRecentMeeting,
                                                              Integer count) {
        if (MapUtils.isEmpty(expertStripGroup)) {
            return Collections.emptyList();
        }
        Map<Long, ExpertUserFullInfo> userMap = CollUtils.listToMap(userFullInfoList, ExpertUserFullInfo::getUserId);
        if (expertsByRecentMeeting.isEmpty()) {
            return expertStripGroup.values().stream()
                    .map(w -> {
                        ExpertUserFullInfo euf;
                        while (true) {
                            ExpertGovBusinessStrip ele = RandomUtil.randomEle(w);
                            euf = userMap.remove(ele.getExpertUserId());
                            if (euf != null || w.isEmpty() || userMap.isEmpty()) {
                                break;
                            }
                            w.remove(ele);
                        }
                        return euf;
                    }).filter(Objects::nonNull)
                    .limit(count)
                    .collect(Collectors.toList());
        } else {
            List<ExpertUserFullInfo> lockedUsers = new ArrayList<>();
            List<String> strips = new ArrayList<>(expertStripGroup.keySet());
            for (int i = 0; i < count; i++) {
                String strip = strips.get(RandomUtils.nextInt(0, strips.size()));
                List<ExpertGovBusinessStrip> expertsByStrip = expertStripGroup.get(strip);
                for (int j = 0; j < expertsByRecentMeeting.size(); j++) {
                    List<MeetingExpert> experts = expertsByRecentMeeting.get(j);
                    List<ExpertUserFullInfo> notInvitedUsers = expertsByStrip.stream()
                            .map(w -> userMap.get(w.getExpertUserId()))
                            .filter(w -> w != null && expertMatchNone(experts, w))
                            .collect(Collectors.toList());
                    if (!notInvitedUsers.isEmpty()) {
                        ExpertUserFullInfo usf = RandomUtil.randomEle(notInvitedUsers);
                        // 防止一个专家多条线
                        userMap.remove(usf.getUserId());
                        lockedUsers.add(usf);
                        break;
                    } else if (j == (expertsByRecentMeeting.size() - 1)) {
                        ExpertUserFullInfo euf;
                        ExpertGovBusinessStrip egs = RandomUtil.randomEle(expertsByStrip);
                        while (true) {
                            euf = userMap.remove(egs.getExpertUserId());
                            if (euf != null || expertsByStrip.isEmpty() || userMap.isEmpty()) {
                                break;
                            }
                            expertsByStrip.remove(egs);
                        }
                        if (euf != null) {
                            lockedUsers.add(euf);
                        }
                    }
                    if (userMap.isEmpty()) {
                        return lockedUsers;
                    }
                }
                if (lockedUsers.size() < count) {
                    strips.remove(strip);
                    if (strips.isEmpty()) {
                        break;
                    }
                }
            }
            return lockedUsers;
        }
    }

    /**
     * 随机抽取专家
     *
     * @param userFullInfos          可抽取的所有专家
     * @param expertsByRecentMeeting 最近会议抽取到的专家
     * @param count                  抽取数量
     * @return 抽取到的专家信息
     * @author WendyYang
     **/
    public static List<ExpertUserFullInfo> inviteWithoutCompany(List<ExpertUserFullInfo> userFullInfos,
                                                                List<List<MeetingExpert>> expertsByRecentMeeting,
                                                                Integer count) {
        List<ExpertUserFullInfo> result;
        if (expertsByRecentMeeting.isEmpty()) {
            result = RandomUtil.randomEleList(userFullInfos, count);
        } else {
            result = new ArrayList<>();
            for (List<MeetingExpert> experts : expertsByRecentMeeting) {
                List<ExpertUserFullInfo> notInvitedUsers = userFullInfos.stream()
                        .filter(w -> expertMatchNone(experts, w))
                        .collect(Collectors.toList());
                if (!notInvitedUsers.isEmpty()) {
                    result.addAll(notInvitedUsers);
                    if (result.size() >= count) {
                        return result.subList(0, count);
                    }
                    userFullInfos.removeAll(notInvitedUsers);
                }
            }
            if (userFullInfos.isEmpty()) {
                return result;
            }
            int restCnt = Math.min(count - result.size(), userFullInfos.size());
            if (userFullInfos.size() > restCnt) {
                result.addAll(RandomUtil.randomEleList(userFullInfos, restCnt));
            } else {
                result.addAll(userFullInfos);
            }
        }
        return result;
    }

}
