package com.qmth.themis.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qmth.themis.business.bean.backend.*;
import com.qmth.themis.business.cache.bean.ExamActivityCacheBean;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dao.*;
import com.qmth.themis.business.entity.TEExamActivity;
import com.qmth.themis.business.enums.BreachTypeEnum;
import com.qmth.themis.business.enums.ExceptionEnum;
import com.qmth.themis.business.enums.VerifyExceptionEnum;
import com.qmth.themis.business.service.TEExamActivityService;
import com.qmth.themis.business.service.TEExamService;
import com.qmth.themis.business.service.TIeReportService;
import com.qmth.themis.business.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class TIeReportServiceImpl implements TIeReportService {

    @Resource
    TEExamStudentMapper examStudentMapper;

    @Resource
    TOeExamRecordMapper examRecordMapper;

    @Resource
    TEExamActivityService examActivityService;

    @Resource
    TEExamService examService;

    @Resource
    TIeInvigilateExceptionInfoMapper invigilateExceptionInfoMapper;

    @Resource
    TBExamInvigilateUserMapper examInvigilateUserMapper;

    @Resource
    TIeInvigilateWarnInfoMapper invigilateWarnInfoMapper;

    @Resource
    TEExamReexamMapper examReexamMapper;

    @Resource
    TEExamBreachLogMapper examBreachLogMapper;

    @Resource
    TEExamStudentLogMapper examStudentLogMapper;

    @Resource
    TOeExamRecordMapper tOeExamRecordMapper;

    @Resource
    private RedisUtil redisUtil;

    @Override
    public Map<String, Object> examView(Long examId, Long examActivityId, String roomCode, String courseCode,
                                        String name, String identity) {
        // 应考人数
        Long totalNum = 0L;
        List<Map<String, Object>> total = examStudentMapper.getTotalCount(examId, examActivityId, roomCode, courseCode);
        Map<Long, Long> totalMap = new HashMap<Long, Long>();
        for (Map<String, Object> map : total) {
            Long acId = (Long) map.get("activityId");
            Long count = (Long) map.get("cc");
            totalMap.put(acId, count);
            totalNum = totalNum + count;
        }
        // 实考人数
        Long doneNum = 0L;
        List<Map<String, Object>> doneCount = examRecordMapper.getDoneCount(examId, examActivityId, roomCode,
                courseCode);
        Map<Long, Long> doneMap = new HashMap<Long, Long>();
        for (Map<String, Object> map : doneCount) {
            Long acId = (Long) map.get("activityId");
            if (acId != null) {
                Long count = (Long) map.get("cc");
                doneMap.put(acId, count);
                doneNum = doneNum + count;
            }

        }
        // 缺考人数
        Map<Long, Long> absentMap = new HashMap<Long, Long>();
        Date now = new Date();
        for (Long acId : totalMap.keySet()) {
            ExamActivityCacheBean ac = examActivityService.getExamActivityCacheBean(acId);
            Long end = ac.getStartTime() + (ac.getOpeningSeconds() * 1000);
            if (now.getTime() > end) {// 场次开考时间结束,未开考的都是缺考
                Long done = doneMap.get(acId);
                if (done == null) {
                    done = 0L;
                }
                absentMap.put(acId, totalMap.get(acId) - done);
            }
        }
        Long absentNum = 0L;
        if (absentMap.size() > 0) {
            for (Long c : absentMap.values()) {
                absentNum = absentNum + c;
            }
        }
        // 每日已考人数
        List<Map<String, Object>> doneCountByDay = examRecordMapper.getDoneCountByDay(examId, examActivityId, roomCode,
                courseCode);

        Map<String, Object> ret = new HashMap<String, Object>();
        ret.put("examTotal", totalNum);
        ret.put("actualExamTotal", doneNum);
        ret.put("deficiencyExamTotal", absentNum);
        ret.put("completeOffExamTotal", totalNum - doneNum - absentNum);
        ret.put("examTotalList", doneCountByDay);

        return ret;
    }

    @Override
    public IPage examViewCount(Long examId, Long examActivityId, String roomCode, String courseCode, String name,
                               String identity, int pageNumber, int pageSize) {
        // 应考人数
        IPage<ExamViewCountListBean> total = examStudentMapper.getTotalCountInfo(new Page<>(pageNumber, pageSize),
                examId, examActivityId, roomCode, courseCode);
        List<ExamViewCountListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        List<Long> activityIds = data.stream().map(e -> e.getExamActivityId()).collect(Collectors.toList());

        // 实考
        List<Map<String, Object>> doneCountByDay = examStudentMapper.getDoneCountByActivityIds(examId, activityIds);
        Map<String, Long> doneMap = new HashMap<String, Long>();
        for (Map<String, Object> map : doneCountByDay) {
            Long acId = (Long) map.get("activityId");
            String roomcode = (String) map.get("roomCode");
            Long count = (Long) map.get("cc");
            doneMap.put(acId + "-" + roomcode, count);
        }

        // 缺考
        List<Long> absentActivityIds = new ArrayList<>();
        Date now = new Date();
        for (Long acid : activityIds) {
            ExamActivityCacheBean ac = examActivityService.getExamActivityCacheBean(acid);
            Long end = ac.getStartTime() + (ac.getOpeningSeconds() * 1000);
            if (now.getTime() > end) {// 场次开考时间结束,未开考的都是缺考
                absentActivityIds.add(acid);
            }
        }

        Map<String, Long> absentMap = new HashMap<String, Long>();
        if (absentActivityIds.size() > 0) {
            List<Map<String, Object>> absentCountByDay = examStudentMapper.getAbsentCountByActivityIds(examId,
                    absentActivityIds);
            for (Map<String, Object> map : absentCountByDay) {
                Long acId = (Long) map.get("activityId");
                String roomcode = (String) map.get("roomCode");
                Long count = (Long) map.get("cc");
                absentMap.put(acId + "-" + roomcode, count);
            }
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamViewCountListBean b : data) {
            Long done = doneMap.get(b.getExamActivityId() + "-" + b.getRoomCode());
            if (done == null) {
                done = 0L;
            }
            Long absent = absentMap.get(b.getExamActivityId() + "-" + b.getRoomCode());
            if (absent == null) {
                absent = 0L;
            }
            b.setActualExamTotal(done);
            b.setDeficiencyExamTotal(absent);
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public IPage examDeficiencyList(Long examId, Long examActivityId, String roomCode, String courseCode,
                                    String name, String identity, int pageNumber, int pageSize) {
        Page<ExamDeficiencyListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("t.exam_activity_id"));
        ipage.addOrder(OrderItem.asc("t.room_code"));
        QueryWrapper<TEExamActivity> wp = new QueryWrapper<>();
        wp.lambda().eq(TEExamActivity::getExamId, examId);
        List<TEExamActivity> acs = examActivityService.list(wp);
        List<Long> absentActivityIds = new ArrayList<>();
        Date now = new Date();
        for (TEExamActivity ac : acs) {
            Long end = ac.getStartTime() + (ac.getOpeningSeconds() * 1000L);
            if (now.getTime() > end) {// 场次开考时间结束,未开考的都是缺考
                absentActivityIds.add(ac.getId());
            }
        }
        if (absentActivityIds.size() == 0) {
            return new Page<>(pageNumber, pageSize);
        }
        IPage<ExamDeficiencyListBean> total = examStudentMapper.getExamDeficiencyPage(ipage, examId, examActivityId,
                roomCode, courseCode, name, identity, absentActivityIds);
        List<ExamDeficiencyListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamDeficiencyListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public IPage examExceptionList(Long examId, Long examActivityId, String roomCode, String courseCode, String name,
                                   String identity, int pageNumber, int pageSize) {
        Page<ExamExceptionListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("f.exam_student_id"));
        IPage<ExamExceptionListBean> total = invigilateExceptionInfoMapper.getExamExceptionPage(ipage, examId,
                examActivityId, roomCode, courseCode, name, identity);
        List<ExamExceptionListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamExceptionListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public List<ExamExceptionDetailListBean> examExceptionDetailList(Long examStudentId) {
        List<ExamExceptionDetailListBean> ret = invigilateExceptionInfoMapper.getExamExceptionDetailList(examStudentId);
        if (ret != null && ret.size() > 0) {
            for (ExamExceptionDetailListBean b : ret) {
                b.setReason(ExceptionEnum.valueOf(b.getReason()).getCode());
            }
        }
        return ret;
    }

    @Override
    public IPage examReexamList(Long examId, Long examActivityId, String roomCode, String courseCode, String name,
                                String identity, int pageNumber, int pageSize) {
        Page<ExamReexamListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("f.exam_student_id"));
        IPage<ExamReexamListBean> total = examReexamMapper.getExamReexamPage(ipage, examId, examActivityId, roomCode,
                courseCode, name, identity);
        List<ExamReexamListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamReexamListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public IPage examBreachList(Long examId, Long examActivityId, String roomCode, String courseCode, String name,
                                String identity, int pageNumber, int pageSize) {
        Page<ExamBreachListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("f.exam_student_id"));
        IPage<ExamBreachListBean> total = examBreachLogMapper.getExamBreachPage(ipage, examId, examActivityId, roomCode,
                courseCode, name, identity);
        List<ExamBreachListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamBreachListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public List<ExamBreachDetailListBean> examBreachListDetail(Long examStudentId) {
        List<ExamBreachDetailListBean> ret = examBreachLogMapper.getExamBreachDetailList(examStudentId);
        if (ret != null && ret.size() > 0) {
            for (ExamBreachDetailListBean b : ret) {
                if (StringUtils.isNotBlank(b.getType())) {
                    b.setType(BreachTypeEnum.valueOf(b.getType()).getCode());
                }
            }
        }
        return ret;
    }

    @Override
    public IPage examRevokeBreachList(Long examId, Long examActivityId, String roomCode, String courseCode,
                                      String name, String identity, int pageNumber, int pageSize) {
        Page<ExamBreachListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("f.exam_student_id"));
        IPage<ExamBreachListBean> total = examBreachLogMapper.getExamRevokeBreachPage(ipage, examId, examActivityId,
                roomCode, courseCode, name, identity);
        List<ExamBreachListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamBreachListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public List<ExamBreachDetailListBean> examRevokeBreachListDetail(Long examStudentId) {
        List<ExamBreachDetailListBean> ret = examBreachLogMapper.getExamBreachDetailList(examStudentId);
        if (ret != null && ret.size() > 0) {
            for (ExamBreachDetailListBean b : ret) {
                if (StringUtils.isNotBlank(b.getType())) {
                    b.setType(BreachTypeEnum.valueOf(b.getType()).getCode());
                }
            }
        }
        return ret;
    }

    @Override
    public IPage examStudentLogList(Long examId, Long examActivityId, String roomCode, String courseCode,
                                    String name, String identity, int pageNumber, int pageSize) {
        Page<ExamStudentLogListBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.asc("t.id"));
        IPage<ExamStudentLogListBean> total = examStudentMapper.getPageForStudentLog(ipage, examId, examActivityId,
                roomCode, courseCode, name, identity);
        List<ExamStudentLogListBean> data = total.getRecords();
        if (data == null || data.size() == 0) {
            return total;
        }
        ExamCacheBean exam = examService.getExamCacheBean(examId);
        for (ExamStudentLogListBean b : data) {
            b.setExamName(exam.getName());
        }
        return total;
    }

    @Override
    public List<ExamStudentLogDetailListBean> examStudentLogListDetail(Long examStudentId) {
        return examStudentLogMapper.getExamStudentLogList(examStudentId);
    }

    /**
     * 在线巡考报表接口
     *
     * @param examId
     * @param userId
     * @return
     */
    @Override
    public List<InvigilateListPatrolReportBean> patrolReport(Long examId, Long userId) {
        return tOeExamRecordMapper.patrolReport(examId, userId);
    }

    /**
     * 考情监控-人数
     */
    @Override
    public ExaminationMonitorCountBean examinationMonitorCount() {
        ExaminationMonitorCountBean ret = new ExaminationMonitorCountBean();
        Integer onlineCount = (Integer) redisUtil.get(SystemConstant.WEBSOCKET_OE_ONLINE_COUNT);
        if (onlineCount == null) {
            onlineCount = 0;
        }
        // 在线人数
        ret.setOnlineCount(Long.valueOf(onlineCount.toString()));
        // 考试人数
        Long examingCount = tOeExamRecordMapper.getExamingCount();
        if (examingCount == null) {
            examingCount = 0L;
        }
        ret.setExamingCount(examingCount);

        // 待考人数
        ret.setWaitingCount(onlineCount - examingCount);

        // 通讯故障人数
        ret.setExceptionCount(tOeExamRecordMapper.getExceptionCount());
        // 预警人数
        ret.setWarnCount(tOeExamRecordMapper.getWarnCount());
        // 机构在考人数
        ret.setOrgExamingCount(tOeExamRecordMapper.getOrgExamingCount());
        return ret;
    }

    /**
     * 考情监控-预警分布
     */
    @Override
    public ExaminationMonitorWarnDistributionBean warnDistribution() {
        ExaminationMonitorWarnDistributionBean ret = new ExaminationMonitorWarnDistributionBean();
        ret.setOrgDistribution(tOeExamRecordMapper.getOrgDistribution());
        List<Map<String, Object>> typeList = tOeExamRecordMapper.getTypeDistribution();
        if (typeList != null && typeList.size() > 0) {
            for (Map<String, Object> map : typeList) {
                map.put("type", VerifyExceptionEnum.valueOf((String) map.get("type")).getCode());
            }
        }
        ret.setTypeDistribution(typeList);
        return ret;
    }

    /**
     * 考情监控-预警时间趋势
     */
    @Override
    public List<ExaminationMonitorHourWarnCountBean> warnTrend() {
        List<ExaminationMonitorHourWarnCountBean> ret = new ArrayList<ExaminationMonitorHourWarnCountBean>();
        SimpleDateFormat sd = new SimpleDateFormat("HH");
        Date start = null;
        Date now = new Date();
        for (int i = 11; i >= 0; i--) {
            ExaminationMonitorHourWarnCountBean b = new ExaminationMonitorHourWarnCountBean();
            Date before = DateUtils.addHours(now, -i);
            b.setHour(sd.format(before));
            b.setCount(0L);
            ret.add(b);
            if (i == 11) {
                start = before;
            }
        }
        start = DateUtils.setMinutes(start, 0);
        start = DateUtils.setSeconds(start, 0);
        start = DateUtils.setMilliseconds(start, 0);
        List<ExaminationMonitorHourWarnCountBean> list = tOeExamRecordMapper.getWarnTrend(start.getTime());
        if (list != null && list.size() > 0) {
            Map<String, Long> map = list.stream().collect(Collectors.toMap(ExaminationMonitorHourWarnCountBean::getHour,
                    ExaminationMonitorHourWarnCountBean::getCount, (key1, key2) -> key2));
            for (ExaminationMonitorHourWarnCountBean b : ret) {
                Long c = map.get(b.getHour());
                if (c != null) {
                    b.setCount(c);
                }
                b.setHour(b.getHour() + ":00");
            }
        }
        return ret;
    }

    @Override
    public List<ExaminationMonitorWarnMsgBean> warnMsg() {
        List<ExaminationMonitorWarnMsgBean> ret = new ArrayList<ExaminationMonitorWarnMsgBean>();
        List<WarningNotifyBean> list = invigilateWarnInfoMapper.findLastMsg(6);
        if (list != null && list.size() > 0) {
            for (WarningNotifyBean b : list) {
                ExaminationMonitorWarnMsgBean bean = new ExaminationMonitorWarnMsgBean();
                bean.setTime(b.getCreateTime());
                StringBuilder sb = new StringBuilder();
                sb.append(b.getName()).append(" （证件号：").append(b.getIdentity()).append("） ").append(b.getInfo());
                List<Map<String, String>> users = examInvigilateUserMapper.getInvigilateUsers(b.getExamId(), b.getRoomCode());
                if (users != null && users.size() > 0) {
                    StringBuilder usersb = new StringBuilder();
                    for (Map<String, String> map : users) {
                        if (map != null) {
                            String lname = map.get("loginName");
                            String name = map.get("name");
                            if (StringUtils.isNotBlank(lname) && StringUtils.isNotBlank(name)) {
                                usersb.append(lname).append("（账号：").append(name).append("） ");
                            }
                        }
                    }
                    if (usersb.length() > 0) {
                        sb.append("，系统已提示").append(usersb).append("进行人工干预");
                    }
                }
                bean.setContent(sb.toString());
                ret.add(bean);
            }
        }
        return ret;
    }
}
