package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.common.util.BizUtils;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.expert.constant.ExpertAccountStatusEnum;
import com.ningdatech.pmapi.expert.entity.ExpertAvoidCompany;
import com.ningdatech.pmapi.expert.entity.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.service.IExpertAvoidCompanyService;
import com.ningdatech.pmapi.expert.service.IExpertIntentionWorkRegionService;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.builder.ExpertInviteBuilder;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.AvoidRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.ExpertChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.ExpertDictChooseDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper;
import com.ningdatech.pmapi.meeting.helper.YxtCallOrSmsHelper;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.meta.helper.TagCache;
import com.ningdatech.pmapi.meta.model.entity.ExpertDictionary;
import com.ningdatech.pmapi.meta.model.entity.ExpertTag;
import com.ningdatech.pmapi.meta.service.IExpertDictionaryService;
import com.ningdatech.pmapi.meta.service.IExpertTagService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ningdatech.pmapi.meeting.helper.ExpertInviteHelper.getExpertInviteRule;

/**
 * <p>
 * 专家邀请管理
 * </p>
 *
 * @author WendyYang
 * @since 18:05 2022/8/8
 */
@Component
@AllArgsConstructor
public class ExpertInviteManage {

    private final IExpertDictionaryService expertDictionaryService;
    private final TagCache tagCache;
    private final IExpertTagService expertTagService;
    private final IExpertIntentionWorkRegionService workRegionService;
    private final IExpertInviteRuleService inviteRuleService;
    private final IMeetingExpertService meetingExpertService;
    private final ExpertInviteHelper expertInviteHelper;
    private final IExpertUserFullInfoService expertUserFullInfoService;
    private final IMeetingService meetingService;
    private final IExpertAvoidCompanyService iExpertAvoidCompanyService;
    private final YxtCallOrSmsHelper yxtCallOrSmsHelper;

    private static final Predicate<Collection<?>> COLL_EMPTY = (coll) -> coll != null && coll.isEmpty();

    private LambdaQueryWrapper<ExpertUserFullInfo> buildBaseExpertQuery() {
        return Wrappers.lambdaQuery(ExpertUserFullInfo.class)
                .select(ExpertUserFullInfo::getUserId,
                        ExpertUserFullInfo::getId,
                        ExpertUserFullInfo::getCompany,
                        ExpertUserFullInfo::getPhoneNo)
                .eq(ExpertUserFullInfo::getExpertAccountStatus, ExpertAccountStatusEnum.AVAILABLE.getKey());
    }

    /**
     * 增加专家层级限制
     *
     * @param query 查询
     * @param rule  随机邀请规则
     */
    private static void addRegionLimit(LambdaQueryWrapper<ExpertUserFullInfo> query, RandomInviteRuleDTO rule) {
        if (ObjectUtils.allNotNull(rule.getExpertRegionCode(), rule.getExpertRegionLevel())) {
            query.eq(ExpertUserFullInfo::getRegionCode, rule.getExpertRegionCode());
            query.eq(ExpertUserFullInfo::getRegionLevel, rule.getExpertRegionLevel());
        }
    }

    /**
     * 获取满足履职意向地的专家ID
     * null -> 表示无需过滤
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByRegion(RandomInviteRuleDTO rule) {
        if (rule.getIntentionRegionCode() == null || rule.getIntentionRegionLevel() == null) {
            return null;
        }
        return workRegionService.userIdsMatchIntentionRegion(rule.getIntentionRegionCode(), rule.getIntentionRegionLevel());
    }


    /**
     * 如果抽取回避单位和专家回避单位一致，同样需要回避该专家
     *
     * @param companyNames /
     * @return /
     */
    private List<Long> avoidCompanyExpertIds(List<String> companyNames) {
        if (CollUtil.isEmpty(companyNames)) {
            return new ArrayList<>();
        }
        List<String> dealCompanyNames = new ArrayList<>();
        for (String companyName : companyNames) {
            if (companyName.contains(",")) {
                String[] splitCompanyNames = companyName.split(",");
                for (String splitCompanyName : splitCompanyNames) {
                    dealCompanyNames.add(splitCompanyName);
                }
            } else {
                dealCompanyNames.add(companyName);
            }
        }
        List<ExpertAvoidCompany> expertAvoidCompanyList = iExpertAvoidCompanyService.list(Wrappers.lambdaQuery(ExpertAvoidCompany.class)
                .in(ExpertAvoidCompany::getCompanyName, dealCompanyNames));
        return expertAvoidCompanyList.stream().map(ExpertAvoidCompany::getUserId).distinct().collect(Collectors.toList());
    }

    private List<Long> mergeExpertIdsByCondition(RandomInviteRuleDTO rule, AvoidRuleDTO avoidInfo) {
        // 处理履职意向地
        List<Long> expertIdsByIntentionRegion = expertIdsByRegion(rule);
        if (COLL_EMPTY.evaluate(expertIdsByIntentionRegion)) {
            return null;
        }
        // 处理专家标签
        List<Long> expertIdsByTag = expertIdsByTag(rule);
        if (COLL_EMPTY.evaluate(expertIdsByTag)) {
            return null;
        }
        // 处理专家字典
        List<Long> expertIdsByDict = expertIdsByDict(rule);
        if (COLL_EMPTY.evaluate(expertIdsByDict)) {
            return null;
        }
        // 处理专家回避单位 与 抽取回避单位是否相同
        List<Long> avoidCompanyExpertIds = avoidCompanyExpertIds(avoidInfo.getAvoidUnitIdList());

        // 聚合用户ID
        List<Long> expertIdsIn = new ArrayList<>();
        if (ObjectUtils.anyNotNull(expertIdsByDict, expertIdsByTag, expertIdsByIntentionRegion)) {
            Optional.ofNullable(expertIdsByDict).ifPresent(expertIdsIn::addAll);
            Optional.ofNullable(expertIdsByTag).ifPresent(item -> {
                if (expertIdsIn.isEmpty()) {
                    expertIdsIn.addAll(expertIdsByTag);
                } else {
                    expertIdsIn.removeIf(w -> !expertIdsByTag.contains(w));
                }
            });
            Optional.ofNullable(expertIdsByIntentionRegion).ifPresent(item -> {
                if (expertIdsIn.isEmpty()) {
                    expertIdsIn.addAll(expertIdsByIntentionRegion);
                } else {
                    expertIdsIn.removeIf(w -> !expertIdsByIntentionRegion.contains(w));
                }
            });
            if (expertIdsIn.isEmpty()) {
                return null;
            }
        }

        if (CollUtil.isNotEmpty(avoidCompanyExpertIds)) {
            for (Long avoidCompanyExpertId : avoidCompanyExpertIds) {
                expertIdsIn.remove(avoidCompanyExpertId);
            }
        }
        return expertIdsIn;
    }

    /**
     * 根据专家标签获取满足的专家ID
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByTag(RandomInviteRuleDTO rule) {
        if (CollectionUtils.isEmpty(rule.getExpertTags())) {
            return null;
        }
        LambdaQueryWrapper<ExpertTag> query = Wrappers.lambdaQuery(ExpertTag.class)
                .select(ExpertTag::getUserId, ExpertTag::getExpertInfoField);
        rule.getExpertTags().forEach(tag -> {
            List<String> codes = tag.getTagCodes().stream()
                    .map(tagCache::listChildrenCodes)
                    .flatMap(Collection::stream)
                    .collect(Collectors.toList());
            query.or(wrapper -> wrapper.eq(ExpertTag::getExpertInfoField, tag.getExpertTag())
                    .in(ExpertTag::getTagCode, codes));
        });
        List<ExpertTag> expertTagList = expertTagService.list(query);
        return expertTagList.stream()
                .collect(Collectors.groupingBy(ExpertTag::getUserId, Collectors.collectingAndThen(Collectors.toList(),
                        tags -> CollUtils.fieldList(tags, ExpertTag::getExpertInfoField).size())))
                .entrySet().stream().filter(w -> w.getValue() == rule.getExpertTags().size())
                .map(Map.Entry::getKey).collect(Collectors.toList());
    }

    /**
     * 根据专家字典获取满足的专家ID
     * null -> 表示无需过虑
     * 空集合 -> 未查到
     *
     * @param rule 抽取规则
     * @return java.util.List<java.lang.Long>
     * @author WendyYang
     **/
    private List<Long> expertIdsByDict(RandomInviteRuleDTO rule) {
        if (CollectionUtils.isEmpty(rule.getExpertDicts())) {
            return null;
        }
        LambdaQueryWrapper<ExpertDictionary> query = Wrappers.lambdaQuery(ExpertDictionary.class)
                .select(ExpertDictionary::getUserId, ExpertDictionary::getExpertInfoField);
        for (ExpertDictChooseDTO dict : rule.getExpertDicts()) {
            query.or(wrapper -> wrapper.eq(ExpertDictionary::getExpertInfoField, dict.getExpertDict())
                    .in(ExpertDictionary::getDictionaryCode, dict.getDictCodes()));
        }
        List<ExpertDictionary> expertDictionaryList = expertDictionaryService.list(query);
        return expertDictionaryList.stream()
                .collect(Collectors.groupingBy(ExpertDictionary::getUserId,
                        Collectors.collectingAndThen(Collectors.toList(),
                                dicts -> CollUtils.fieldList(dicts, ExpertDictionary::getExpertInfoField).size())))
                .entrySet().stream().filter(w -> w.getValue() == rule.getExpertDicts().size())
                .map(Map.Entry::getKey).collect(Collectors.toList());
    }


    /**
     * 专家抽取（随机）
     *
     * @param avoidRule        回避信息
     * @param randomRule       抽取规则
     * @param appointExpertIds 指定抽取专家ID
     * @return 满足抽取条件的专家
     * @author WendyYang
     **/
    public ExpertChooseDTO expertInviteByRandomRule(AvoidRuleDTO avoidRule,
                                                    RandomInviteRuleDTO randomRule,
                                                    List<Long> appointExpertIds,
                                                    LocalDateTime start,
                                                    LocalDateTime end) {
        ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
        List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidRule);
        if (expertIdsIn == null) {
            return result;
        }
        boolean avoidExpert = CollUtil.isNotEmpty(avoidRule.getExpertIds());
        boolean avoidCompany = CollUtil.isNotEmpty(avoidRule.getAvoidUnitIdList());
        Set<String> tmpAvoidCompany = new HashSet<>();
        if (avoidCompany) {
            List<String> companyIds = avoidRule.getAvoidUnitIdList();
            for (String companyId : companyIds) {
                if (companyId.contains(",")) {
                    String[] splitCompanyIds = companyId.split(",");
                    Collections.addAll(tmpAvoidCompany, splitCompanyIds);
                } else {
                    tmpAvoidCompany.add(companyId);
                }
            }
        }
        // 回避信息
        LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
        query.notIn(!tmpAvoidCompany.isEmpty(), ExpertUserFullInfo::getCompany, tmpAvoidCompany);
        if (avoidCompany) {
            query.notExists("select 1 from expert_avoid_company eac where eac.user_id = expert_user_full_info.user_id" +
                    " and company_name in ({0})", CollUtils.joinByComma(avoidRule.getAvoidUnitIdList()));
        }
        // 处理专家层级
        addRegionLimit(query, randomRule);
        if (!expertIdsIn.isEmpty()) {
            if (avoidExpert) {
                expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
                if (expertIdsIn.isEmpty()) {
                    // 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
                    // 如果为空则说明没有符合条件的
                    return result;
                }
            }
            if (CollUtil.isNotEmpty(appointExpertIds)) {
                expertIdsIn.removeIf(appointExpertIds::contains);
                if (expertIdsIn.isEmpty()) {
                    return result;
                }
            }
            // 过滤掉已参加会议的专家
            List<Long> expertIdsLockByMeeting = expertInviteHelper.listInvitedExpertByTime(start, end);
            expertIdsIn.removeIf(expertIdsLockByMeeting::contains);
            if (expertIdsIn.isEmpty()) {
                return result;
            }
            query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
        } else if (avoidExpert || CollUtil.isNotEmpty(appointExpertIds)) {
            Set<Long> tempExperts = expertInviteHelper.getAvoidExpert(appointExpertIds, avoidRule, start, end);
            query.notIn(ExpertUserFullInfo::getUserId, tempExperts);
        } else {
            Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
            if (!notInUserIds.isEmpty()) {
                query.notIn(ExpertUserFullInfo::getUserId, notInUserIds);
            }
        }
        List<ExpertUserFullInfo> userInfoList = expertUserFullInfoService.list(query);
        if (userInfoList.isEmpty()) {
            return result;
        }
        Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userInfoList, ExpertUserFullInfo::getCompany);
        result.setTotal(userInfoList.size());
        // count为空表示数量校验
        if (randomRule.getCount() == null || result.getTotal() >= randomRule.getCount()) {
            result.setExperts(inviteGroupByCompany(userGroupByUnit, randomRule.getCount()));
        }
        return result;
    }

    /**
     * 专家替换、补充
     *
     * @param avoidRule      回避信息
     * @param randomRule     随机抽取
     * @param invitedExperts 已抽取人员
     * @param count          抽取数量
     * @param start          会议开始时间
     * @param end            会议结束时间
     * @return {@link ExpertChooseDTO}
     * @author WendyYang
     **/
    public ExpertChooseDTO expertReplaceByRandomRule(AvoidRuleDTO avoidRule,
                                                     RandomInviteRuleDTO randomRule,
                                                     Collection<MeetingExpert> invitedExperts,
                                                     Integer count,
                                                     LocalDateTime start,
                                                     LocalDateTime end,
                                                     boolean invitedRefused) {
        ExpertChooseDTO result = new ExpertChooseDTO(new ArrayList<>(), 0);
        // 合并标签、字典
        List<Long> expertIdsIn = mergeExpertIdsByCondition(randomRule, avoidRule);
        if (expertIdsIn == null) {
            return result;
        }
        LambdaQueryWrapper<ExpertUserFullInfo> query = buildBaseExpertQuery();
        query.notIn(ExpertUserFullInfo::getCompany, avoidRule.getAvoidUnitIdList());
        query.notExists("select 1 from expert_avoid_company eac where eac.user_id = expert_user_full_info.user_id" +
                " and company_name in ({0})", CollUtils.joinByComma(avoidRule.getAvoidOrgIdList()));

        // 处理专家层级
        if (StrUtils.isNotBlank(randomRule.getExpertRegionCode())) {
            query.eq(ExpertUserFullInfo::getRegionCode, randomRule.getExpertRegionCode());
            query.eq(ExpertUserFullInfo::getRegionLevel, randomRule.getExpertRegionLevel());
        }
        if (expertIdsIn.size() > 0) {
            if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds())) {
                expertIdsIn.removeIf(w -> avoidRule.getExpertIds().contains(w));
                if (expertIdsIn.isEmpty()) {
                    // 字典、标签、履职意向地筛选出的专家ID移除需要回避的专家ID
                    // 如果为空则说明没有符合条件的
                    return result;
                }
            }
            List<Long> lockExpertIds = expertInviteHelper.listInvitedExpertByTime(start, end);
            expertIdsIn.removeIf(lockExpertIds::contains);
            if (expertIdsIn.isEmpty()) {
                return result;
            }
            query.in(ExpertUserFullInfo::getUserId, expertIdsIn);
        } else if (CollUtil.isNotEmpty(avoidRule.getExpertIds())) {
            Set<Long> notInExpertIds = expertInviteHelper.getAvoidExpert(avoidRule.getExpertIds(), null, start, end);
            query.notIn(ExpertUserFullInfo::getUserId, notInExpertIds);
        } else {
            Set<Long> notInUserIds = expertInviteHelper.listExpertLeaveOrInvited(start, end);
            if (notInUserIds.size() > 0) {
                query.notIn(ExpertUserFullInfo::getUserId, notInUserIds);
            }
        }
        List<ExpertUserFullInfo> userFullInfos = expertUserFullInfoService.list(query);
        if (userFullInfos.size() == 0) {
            return result;
        }
        Map<ExpertAttendStatusEnum, List<MeetingExpert>> expertGroupByStatus = invitedExperts.stream()
                .collect(Collectors.groupingBy(w -> ExpertAttendStatusEnum.getByCode(w.getStatus())));
        // 回避同单位其他专家
        List<MeetingExpert> removeExpertByCompany = new ArrayList<>();
        BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.AGREED), removeExpertByCompany::addAll);
        BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.NOTICING), removeExpertByCompany::addAll);
        List<Long> removeExpertIds = new ArrayList<>();
        if (invitedRefused) {
            // 拒绝参加的不可以被再次抽中
            BizUtils.notEmpty(expertGroupByStatus.get(ExpertAttendStatusEnum.REFUSED), w -> {
                List<Long> tempRefused = CollUtils.fieldList(w, MeetingExpert::getExpertId);
                removeExpertIds.addAll(tempRefused);
            });
        }
        List<Long> tempExpertIds = CollUtils.fieldList(removeExpertByCompany, MeetingExpert::getExpertId);
        // 移除确认参加、通知中的、拒绝参加、已取消
        userFullInfos.removeIf(w -> tempExpertIds.contains(w.getUserId()) || removeExpertIds.contains(w.getUserId()));
        Map<String, List<ExpertUserFullInfo>> userGroupByUnit = CollUtils.group(userFullInfos, ExpertUserFullInfo::getCompany);
        result.setTotal(userGroupByUnit.size());
        result.setExperts(inviteGroupByCompany(userGroupByUnit, count));
        return result;
    }

    private List<List<MeetingExpert>> selectMeetingExpertByCount() {
        LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
                .select(Meeting::getId)
                .orderByDesc(Meeting::getCreateOn)
                .last("limit " + 5);
        List<Long> meetingIds = CollUtils.fieldList(meetingService.list(query), Meeting::getId);
        if (meetingIds.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, List<MeetingExpert>> groupByMeetingId = meetingExpertService.listByMeetingIds(meetingIds)
                .stream().collect(Collectors.groupingBy(MeetingExpert::getMeetingId));
        return groupByMeetingId.entrySet().stream()
                .sorted(Map.Entry.comparingByKey(Comparator.reverseOrder()))
                .map(Map.Entry::getValue)
                .collect(Collectors.toList());
    }

    /**
     * 每个单位只抽取一人
     *
     * @param expertGroupByUnit 需要抽取的人
     * @param count             抽取数量
     * @return java.util.List<com.ningdatech.emapi.expert.entity.domain.ExpertUserFullInfo>
     * @author WendyYang
     **/
    private List<ExpertUserFullInfo> inviteGroupByCompany(Map<String, List<ExpertUserFullInfo>> expertGroupByUnit, Integer count) {
        if (MapUtils.isEmpty(expertGroupByUnit)) {
            return Collections.emptyList();
        }
        List<List<MeetingExpert>> meetingExperts = selectMeetingExpertByCount();
        if (meetingExperts.isEmpty()) {
            return expertGroupByUnit.values().stream()
                    .map(expertUsers -> expertUsers.get(RandomUtils.nextInt(0, expertUsers.size())))
                    .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 key = keySet.get(RandomUtils.nextInt(0, keySet.size()));
                List<ExpertUserFullInfo> expertUserFullInfos = expertGroupByUnit.get(key);
                for (List<MeetingExpert> expertList : meetingExperts) {
                    List<ExpertUserFullInfo> tempList = expertUserFullInfos.stream()
                            .filter(w -> expertList.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId())))
                            .collect(Collectors.toList());
                    if (!tempList.isEmpty()) {
                        result.add(tempList.get(RandomUtils.nextInt(0, tempList.size())));
                        break;
                    } else if (meetingExperts.indexOf(expertList) == (meetingExperts.size() - 1)) {
                        result.add(expertUserFullInfos.get(RandomUtils.nextInt(0, expertUserFullInfos.size())));
                    }
                }
                if (result.size() < count) {
                    keySet.remove(key);
                    if (keySet.isEmpty()) {
                        break;
                    }
                }
            }
            return result;
        }
    }

    private List<ExpertUserFullInfo> inviteWithoutCompany(List<ExpertUserFullInfo> userFullInfos, Integer count) {
        List<ExpertUserFullInfo> result = new ArrayList<>();
        List<List<MeetingExpert>> meetingExpertList = selectMeetingExpertByCount();
        if (meetingExpertList.isEmpty()) {
            for (int i = 0; i < count; i++) {
                int randomIndex = RandomUtils.nextInt(0, userFullInfos.size());
                result.add(userFullInfos.remove(randomIndex));
                if (userFullInfos.size() == 0) {
                    break;
                }
            }
        } else {
            for (List<MeetingExpert> meetingExperts : meetingExpertList) {
                List<ExpertUserFullInfo> unSelectedUsers = userFullInfos.stream()
                        .filter(w -> meetingExperts.stream().noneMatch(expert -> expert.getExpertId().equals(w.getUserId())))
                        .collect(Collectors.toList());
                if (!unSelectedUsers.isEmpty()) {
                    result.addAll(unSelectedUsers);
                    if (result.size() >= count) {
                        return result.subList(0, count);
                    }
                    userFullInfos.removeAll(unSelectedUsers);
                }
            }
            if (userFullInfos.size() == 0) {
                return result;
            }
            int restCount = Math.min(count - result.size(), userFullInfos.size());
            int groupCount = userFullInfos.size() / restCount;
            if (userFullInfos.size() > restCount) {
                Stream.iterate(0, t -> t + 1).limit(restCount)
                        .forEach(t -> {
                            int start = t * groupCount;
                            int end = start + groupCount;
                            if (end >= groupCount * restCount) {
                                end = userFullInfos.size();
                            }
                            result.add(userFullInfos.get(RandomUtils.nextInt(start, end)));
                        });
            } else {
                result.addAll(userFullInfos);
            }
        }
        return result;
    }

    /**
     * 专家抽取（会议创建时抽取）
     *
     * @param randomRules 随机抽取规则
     * @param avoidRuled  回避信息
     * @param meeting     会议信息
     * @author WendyYang
     **/
    public void expertInviteByMeetingCreate(Meeting meeting,
                                            List<RandomInviteRuleDTO> randomRules,
                                            AvoidRuleDTO avoidRuled) {
        List<MeetingExpert> expertInserts = new ArrayList<>();
        // 处理随机抽取规则
        if (CollectionUtils.isNotEmpty(randomRules)) {
            List<ExpertInviteRule> randoms = new ArrayList<>();
            List<ExpertChooseDTO> expertsByRandom = new ArrayList<>();
            List<Long> chooseExpertIds = new ArrayList<>();
            LocalDateTime startTime = meeting.getStartTime();
            LocalDateTime endTime = meeting.getEndTime();
            randomRules.forEach(rule -> {
                ExpertChooseDTO tempExperts = expertInviteByRandomRule(avoidRuled, rule, chooseExpertIds, startTime, endTime);
                expertsByRandom.add(tempExperts);
                chooseExpertIds.addAll(CollUtils.fieldList(tempExperts.getExperts(), ExpertUserFullInfo::getUserId));
                randoms.add(getExpertInviteRule(rule, meeting.getId()));
            });
            inviteRuleService.saveBatch(randoms);
            for (int i = 0; i < expertsByRandom.size(); i++) {
                Long ruleId = randoms.get(i).getId();
                expertsByRandom.get(i).getExperts().forEach(w -> {
                    MeetingExpert expert = ExpertInviteBuilder.getExpertByRandom(meeting.getId(), w, ruleId);
                    expert.setStatus(ExpertAttendStatusEnum.NOTICING.getCode());
                    expertInserts.add(expert);
                });
            }
            yxtCallOrSmsHelper.callByMeetingExperts(meeting, expertInserts);
        }
        meetingExpertService.saveBatch(expertInserts);
    }

}
