package com.ningdatech.pmapi.meeting.helper;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ningdatech.basic.exception.BizException;
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.ExpertUserFullInfo;
import com.ningdatech.pmapi.expert.service.ExpertInfoService;
import com.ningdatech.pmapi.expert.service.IExpertUserFullInfoService;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteAvoidRule;
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.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingBasicDTO;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVO;
import com.ningdatech.pmapi.meeting.entity.vo.MeetingByManagerVO;
import com.ningdatech.pmapi.meeting.service.IExpertInviteAvoidRuleService;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.meta.model.dto.DictionaryDTO;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * MeetingManageHelper
 * </p>
 *
 * @author WendyYang
 * @since 17:23 2022/8/23
 */
@Component
@AllArgsConstructor
public class MeetingManageHelper {

    private final DictionaryCache dictionaryCache;
    private final ExpertInfoService expertInfoService;
    private final IExpertUserFullInfoService expertUserFullInfoService;
    private final IMeetingExpertService meetingExpertService;
    private final IExpertInviteRuleService inviteRuleService;
    private final IExpertInviteAvoidRuleService inviteAvoidRuleService;

    public void checkReviewProject(MeetingBasicDTO meetingBasic) {
        if (meetingBasic.getIsInnerProject()) {
            Assert.notEmpty(meetingBasic.getProjectIdList(), "评审项目不能为空");
        } else {
            Assert.notEmpty(meetingBasic.getProjects(), "评审项目不能为空");
        }
    }

    /**
     * 校验会议是否还可继续抽取
     */
    public boolean checkCouldBeInvitedContinue(Long meetingId) {
        Map<Long, RandomInviteRuleDTO> ruleMap = inviteRuleService.randomRuleByMeetingId(meetingId);
        List<MeetingExpert> experts = meetingExpertService.listExpertLastByMeetingId(meetingId);
        if (experts.isEmpty()) {
            return Boolean.TRUE;
        }
        Map<Long, List<MeetingExpert>> expertMap = CollUtils.group(experts, MeetingExpert::getRuleId);
        for (Map.Entry<Long, RandomInviteRuleDTO> entry : ruleMap.entrySet()) {
            List<MeetingExpert> tmpExperts = expertMap.get(entry.getKey());
            if (CollUtil.isEmpty(tmpExperts)) {
                return Boolean.TRUE;
            }
            int count = 0;
            for (MeetingExpert w : tmpExperts) {
                boolean status = ExpertAttendStatusEnum.AGREED.eq(w.getStatus()) ||
                        ExpertAttendStatusEnum.NOTICING.eq(w.getStatus());
                if (status) {
                    count++;
                }
            }
            if (count < entry.getValue().getCount()) {
                return Boolean.TRUE;
            }
        }
        return Boolean.FALSE;
    }

    /**
     * 获取专家出席会议的状态
     *
     * @param info 会议状态及评价信息
     * @return java.lang.Integer
     * @author WendyYang
     **/
    public Integer getExpertAttendStatus(MeetingAndAttendStatusDTO info) {
        // TODO
        return null;
    }

    public MeetingByManagerVO buildByMeeting(Meeting meeting) {
        DictionaryDTO dict = dictionaryCache.getByCode(meeting.getType());
        return MeetingByManagerVO.builder()
                .meetingId(meeting.getId())
                .meetingTypeName(dict == null ? "" : dict.getName())
                .meetingName(meeting.getName())
                .startTime(meeting.getStartTime())
                .endTime(meeting.getEndTime())
                .inviteType(meeting.getInviteType())
                .confirmedRoster(meeting.getConfirmedRoster())
                .inviteStatus(meeting.getInviteStatus())
                .status(meeting.getStatus())
                .build();
    }

    public void buildMeetingQuery(LambdaQueryWrapper<Meeting> query, MeetingListReq req) {
        if (StrUtil.isNotBlank(req.getName())) {
            query.like(Meeting::getName, req.getName());
        }
        if (StrUtil.isNotBlank(req.getMeetingType())) {
            query.eq(Meeting::getType, req.getMeetingType());
        }
        if (req.getStartTime() != null) {
            query.ge(Meeting::getStartTime, req.getStartTime());
        }
        if (req.getEndTime() != null) {
            query.le(Meeting::getEndTime, req.getEndTime());
        }
        if (StrUtils.isNotBlank(req.getProjectName())) {
            String projectName = "%" + req.getProjectName() + "%";
            query.and(q1 -> q1.exists("select 1 from nd_project np inner join meeting_inner_project mip on mip.project_id = np.id" +
                            " where mip.meeting_id = meeting.id and np.project_name like {0}", projectName)
                    .or(q2 -> q2.exists("select 1 from meeting_outer_project mop where mop.meeting_id = meeting.id" +
                            " and mop.project_name like {0}", projectName)));
        }
    }

    /**
     * 构建用户基本信息
     *
     * @param userIds 用户ID
     * @return java.util.Map<java.lang.Long, com.ningdatech.pmapi.meeting.entity.vo.ExpertBasicInfoVo>
     * @author WendyYang
     **/
    public Map<Long, ExpertBasicInfoVO> getExpertBasicInfo(List<Long> userIds) {
        /*List<ExpertFullInfoAllDTO> expertInfos = expertInfoService.listExpertUserFullInfoAll(userIds);
        return CollUtils.listToMap(expertInfos, ExpertFullInfoAllDTO::getUserId, w -> {
            ExpertBasicInfoVO basicInfoVo = new ExpertBasicInfoVO();
            ExpertUserFullInfoDTO userInfo = w.getExpertUserInfoDTO();
            basicInfoVo.setName(userInfo.getName());
            basicInfoVo.setExpertId(w.getUserId());
            basicInfoVo.setContact(userInfo.getPhoneNo());
            basicInfoVo.setCompany(userInfo.getCompany());
            Optional<ExpertDictionaryDTO> first = w.getExpertDictionaryList().stream()
                    .filter(dict -> dict.getExpertInfoField().equals(DictExpertInfoTypeEnum.TITLE_LEVEL.getKey()))
                    .findFirst();
            if (first.isPresent()) {
                DictionaryDTO dictInfo = dictionaryCache.getByCode(first.get().getDictionaryCode());
                basicInfoVo.setJobLevel(dictInfo.getName());
            } else {
                basicInfoVo.setJobLevel("");
            }
            basicInfoVo.setCompanyType("");
            return basicInfoVo;
        });*/
        // TODO
        return null;
    }

    public AvoidRuleDTO getAvoidInfoDto(Long meetingId) {
        ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId);
        AvoidRuleDTO result = new AvoidRuleDTO();
        result.setAvoidOrgIdList(StrUtil.split(avoidRule.getAvoidOrgIds(), ","));
        result.setExpertIds(BizUtils.splitToLong(avoidRule.getAvoidExpertIds()));
        result.setAvoidUnitIdList(StrUtil.split(avoidRule.getAvoidUnitIds(), ","));
        result.setWeekInviteCount(result.getWeekInviteCount());
        return result;
    }

    public void saveAvoidInfo(Long meetingId, List<Long> expertIds) {
        ExpertInviteAvoidRule avoidRule = inviteAvoidRuleService.getByMeetingId(meetingId);
        avoidRule.setAvoidExpertIds(CollUtils.joinByComma(expertIds));
        inviteAvoidRuleService.saveOrUpdate(avoidRule);
    }

    /**
     * 校验是否能够进行指定邀请
     *
     * @param meetingId 会议ID
     * @param expertIds 专家ID
     * @return 符合邀请规则的专家
     * @author WendyYang
     **/
    public List<ExpertUserFullInfo> appointExpertCheck(Long meetingId, List<Long> expertIds) {
        List<ExpertUserFullInfo> experts = expertUserFullInfoService.listByUserId(expertIds);
        AvoidRuleDTO avoidRule = getAvoidInfoDto(meetingId);
        experts.forEach(expert -> {
            if (avoidRule.getAvoidUnitIdList().contains(expert.getCompany())) {
                throw BizException.wrap("请移除已回避单位的专家");
            }
            if (CollectionUtils.isNotEmpty(avoidRule.getExpertIds()) && avoidRule.getExpertIds().contains(expert.getUserId())) {
                throw BizException.wrap("请移除已回避的专家");
            }
            // 校验专家状态
            ExpertAccountStatusEnum accountStatus = ExpertAccountStatusEnum.of(expert.getExpertAccountStatus());
            if (!accountStatus.equals(ExpertAccountStatusEnum.AVAILABLE)) {
                throw BizException.wrap("专家%s不可被抽取", expert.getExpertName());
            }
        });
        Map<Long, ExpertUserFullInfo> expertMap = CollUtils.listToMap(experts, ExpertUserFullInfo::getUserId);
        List<MeetingExpert> meetingExperts = meetingExpertService.listByMeetingId(meetingId);
        if (!meetingExperts.isEmpty()) {
            Comparator<MeetingExpert> comparator = Comparator.comparing(MeetingExpert::getUpdateOn).reversed();
            Collection<MeetingExpert> values = BizUtils.groupFirst(meetingExperts, MeetingExpert::getExpertId, comparator);
            values.forEach(w -> {
                ExpertUserFullInfo expertInfo = expertMap.get(w.getExpertId());
                if (expertInfo != null) {
                    String expertName = expertInfo.getExpertName();
                    switch (ExpertAttendStatusEnum.getByCode(w.getStatus())) {
                        case REFUSED:
                            throw BizException.wrap("专家%s已拒绝参加", expertName);
                        case AGREED:
                            throw BizException.wrap("专家%s已同意参加", expertName);
                        case NOTICING:
                            throw BizException.wrap("专家%s正在通知中", expertName);
                        default:
                            break;
                    }
                }
            });
        }
        return experts;
    }

}
