package com.ningdatech.pmapi.meeting.manage;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.model.PagePo;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.CollUtils;
import com.ningdatech.pmapi.meeting.entity.domain.Meeting;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.CountConfirmByMeetingIdDTO;
import com.ningdatech.pmapi.meeting.entity.dto.MeetingAndAttendStatusDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusByDashboard;
import com.ningdatech.pmapi.meeting.entity.enumeration.MeetingStatusEnum;
import com.ningdatech.pmapi.meeting.entity.req.MeetingCalenderReq;
import com.ningdatech.pmapi.meeting.entity.req.MeetingListReq;
import com.ningdatech.pmapi.meeting.entity.vo.*;
import com.ningdatech.pmapi.meeting.helper.MeetingManageHelper;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertEvaluationService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.pmapi.meeting.service.IMeetingService;
import com.ningdatech.pmapi.meta.helper.DictionaryCache;
import com.ningdatech.pmapi.user.util.LoginUserUtil;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * DashboardManage
 * </p>
 *
 * @author WendyYang
 * @since 23:04 2022/8/24
 */
@Component
@AllArgsConstructor
public class DashboardManage {

    private final MeetingManage meetingManage;
    private final IMeetingService meetingService;
    private final IMeetingExpertService meetingExpertService;
    private final MeetingManageHelper meetingManageHelper;
    private final IMeetingExpertEvaluationService meetingExpertEvaluationService;
    private final DictionaryCache dictionaryCache;

    public static final int MEETING_CALENDER_MONTHS = 2;

    public List<MeetingCalenderItemVO> meetingCalender(MeetingCalenderReq po) {
        if (po.getStartDate().plusMonths(MEETING_CALENDER_MONTHS).isBefore(po.getEndDate())) {
            throw new BizException("最多可查询两个月数据");
        }
        MeetingListReq meetingListPo = new MeetingListReq();
        meetingListPo.setPageSize(500);
        meetingListPo.setStartTime(po.getStartDate().atStartOfDay());
        meetingListPo.setEndTime(po.getEndDate().atTime(LocalTime.MAX));
        PageVo<MeetingByManagerVO> meetingPage = meetingManage.meetingListByExpert(meetingListPo);
        Map<LocalDate, List<MeetingByManagerVO>> meetingByDate = meetingPage.getRecords().stream().map(w -> {
                    List<Pair<LocalDate, MeetingByManagerVO>> pairs = new ArrayList<>();
                    LocalDateTime tempTime = w.getStartTime();
                    while (tempTime.isBefore(w.getEndTime())) {
                        pairs.add(Pair.of(tempTime.toLocalDate(), w));
                        tempTime = tempTime.plusDays(1);
                    }
                    return pairs;
                }).flatMap(Collection::stream)
                .collect(Collectors.groupingBy(Pair::getLeft,
                        Collectors.collectingAndThen(Collectors.mapping(Pair::getRight, Collectors.toList()),
                                w -> {
                                    w.sort(Comparator.comparing(MeetingByManagerVO::getStartTime));
                                    return w;
                                })));
        LocalDate tempDate = po.getStartDate();
        List<MeetingCalenderItemVO> result = new ArrayList<>();
        while (!tempDate.isAfter(po.getEndDate())) {
            MeetingCalenderItemVO item = new MeetingCalenderItemVO();
            List<MeetingByManagerVO> meetings = meetingByDate.get(tempDate);
            item.setHasMeeting(meetings != null);
            if (item.getHasMeeting()) {
                item.setMeetings(meetings);
            }
            item.setToday(tempDate);
            result.add(item);
            tempDate = tempDate.plusDays(1);
        }
        return result;
    }

    public PageVo<ExpertEvaluationToDoListItemVO> expertEvaluationToDo(PagePo po) {
        PageVo<ExpertEvaluationToDoListItemVO> result = PageVo.of(new ArrayList<>(), 0L);
        // 查询所有未完成的项目
        LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
                .select(Meeting::getId)
                .ne(Meeting::getStatus, MeetingStatusEnum.CANCELED.getCode())
                .eq(Meeting::getCreateBy, LoginUserUtil.getUserId());
        List<Meeting> meetings = meetingService.list(query);
        if (meetings.isEmpty()) {
            return PageVo.empty();
        }
        List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
        Page<MeetingExpert> page = meetingExpertEvaluationService.pageExpertEvaluationTodo(meetingIds, po);
        if (page.getTotal() > 0) {
            List<Long> expertIds = new ArrayList<>();
            List<Long> meetingIdsByPage = new ArrayList<>();
            page.getRecords().forEach(w -> {
                meetingIdsByPage.add(w.getMeetingId());
                expertIds.add(w.getExpertId());
            });
            Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetingService.listByIds(meetingIdsByPage), Meeting::getId);
            Map<Long, ExpertBasicInfoVO> basicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds);
            page.getRecords().forEach(w -> {
                ExpertBasicInfoVO expertInfo = basicInfoVoMap.get(w.getExpertId());
                Meeting meeting = meetingMap.get(w.getMeetingId());
                ExpertEvaluationToDoListItemVO item = BeanUtil.copyProperties(expertInfo, ExpertEvaluationToDoListItemVO.class);
                item.setMeetingName(meeting.getName());
                item.setMeetingId(w.getMeetingId());
                item.setExpertMeetingId(w.getId());
                item.setStartTime(meeting.getStartTime());
                item.setEndTime(meeting.getEndTime());
                result.getRecords().add(item);
            });
            result.setTotal(page.getTotal());
        }
        return result;
    }

    public PageVo<MeetingConfirmToDoListItemVO> expertConfirmToDo(PagePo po) {
        // 查询所有未完成的项目
        LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
                .eq(Meeting::getCreateBy, LoginUserUtil.getUserId())
                .orderByDesc(Meeting::getStartTime);
        List<Meeting> meetings = meetingService.list(query);
        if (meetings.isEmpty()) {
            return PageVo.empty();
        }
        List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
        Map<Long, CountConfirmByMeetingIdDTO> confirmedMap = meetingExpertService.countConfirmedByMeetingIds(meetingIds);
        confirmedMap.entrySet().removeIf(w -> {
            CountConfirmByMeetingIdDTO confirm = w.getValue();
            return confirm.getConfirmed().equals(confirm.getTotal());
        });
        if (confirmedMap.size() == 0) {
            return PageVo.empty();
        }
        meetings.removeIf(w -> !confirmedMap.containsKey(w.getId()));
        List<MeetingConfirmToDoListItemVO> dataList = meetings.stream()
                .skip((long) (po.getPageNumber() - 1) * po.getPageSize())
                .limit(po.getPageSize()).map(w -> {
                    MeetingConfirmToDoListItemVO item = new MeetingConfirmToDoListItemVO();
                    CountConfirmByMeetingIdDTO confirm = confirmedMap.get(w.getId());
                    if (confirm == null) {
                        item.setTotalExpert(0);
                        item.setConfirmedExpert(0);
                    } else {
                        item.setTotalExpert(confirm.getTotal());
                        item.setConfirmedExpert(confirm.getConfirmed());
                    }
                    item.setMeetingId(w.getId());
                    item.setEndTime(w.getEndTime());
                    item.setStartTime(w.getStartTime());
                    item.setStatus(w.getStatus());
                    item.setType(dictionaryCache.getByCode(w.getType()).getName());
                    item.setMeetingName(w.getName());
                    return item;
                }).collect(Collectors.toList());
        return PageVo.of(dataList, meetings.size());
    }

    public PageVo<ExpertReplaceTodoListItemVO> expertReplaceTodoList(PagePo po) {
        // 查询所有未完成的项目
        LambdaQueryWrapper<Meeting> query = Wrappers.lambdaQuery(Meeting.class)
                .eq(Meeting::getCreateBy, LoginUserUtil.getUserId())
                .orderByDesc(Meeting::getStartTime);
        List<Meeting> meetings = meetingService.list(query);
        if (meetings.isEmpty()) {
            return PageVo.empty();
        }
        List<Long> meetingIds = CollUtils.fieldList(meetings, Meeting::getId);
        Page<MeetingExpert> page = meetingExpertService.pageExpertByStatusAndMeetingIds(po.page(), meetingIds, null);
        if (page.getTotal() == 0) {
            return PageVo.empty();
        }
        Map<Long, Meeting> meetingMap = CollUtils.listToMap(meetings, Meeting::getId);
        List<Long> expertIds = CollUtils.fieldList(page.getRecords(), MeetingExpert::getExpertId);
        Map<Long, ExpertBasicInfoVO> basicInfoVoMap = meetingManageHelper.getExpertBasicInfo(expertIds);
        List<ExpertReplaceTodoListItemVO> dataList = page.getRecords().stream().map(w -> {
            ExpertBasicInfoVO basicInfoVo = basicInfoVoMap.get(w.getExpertId());
            ExpertReplaceTodoListItemVO item = BeanUtil.copyProperties(basicInfoVo, ExpertReplaceTodoListItemVO.class);
            item.setMeetingId(w.getMeetingId());
            item.setExpertMeetingId(w.getId());
            Meeting meeting = meetingMap.get(w.getMeetingId());
            item.setMeetingName(meeting.getName());
            item.setInviteType(ExpertInviteTypeEnum.getByCode(w.getInviteType()).getName());
            item.setStatus(w.getStatus());
            item.setStartTime(meeting.getStartTime());
            item.setEndTime(meeting.getEndTime());
            return item;
        }).collect(Collectors.toList());
        return PageVo.of(dataList, page.getTotal());
    }

    public MeetingCountSummaryVO meetingCountSummary() {
        Map<MeetingStatusByDashboard, Integer> meetingCountSummary = meetingService.meetingCountSummary(LoginUserUtil.getUserId());
        return MeetingCountSummaryVO.builder()
                .canceled(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.CANCELED, 0))
                .completed(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.COMPLETED, 0))
                .toBeHeld(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.TO_BE_HELD, 0))
                .held(meetingCountSummary.getOrDefault(MeetingStatusByDashboard.HELD, 0))
                .build();
    }

    public MeetingCountByExpertVO meetingCountByExpert() {
        List<MeetingAndAttendStatusDTO> attendStatusList = meetingExpertService.listByExpertIdAndStatus(LoginUserUtil.getUserId(), null, null);
        MeetingCountByExpertVO result = MeetingCountByExpertVO.init();
        attendStatusList.forEach(w -> {
            if (w.getAttended() != null && w.getAttended()) {
                result.incrAttended();
            } else {
                result.incrToBeAttended();
            }
        });
        return result;
    }


}
