package com.qmth.jkserver.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.cache.JkCacheService;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.dao.ExamStudentDao;
import com.qmth.jkserver.dto.QueryResultDto;
import com.qmth.jkserver.dto.examProgress.ExamProgressDto;
import com.qmth.jkserver.dto.examProgress.ExamProgressStatisticDto;
import com.qmth.jkserver.enums.BlurryQueryTypeEnum;
import com.qmth.jkserver.enums.ExamConditionEnum;
import com.qmth.jkserver.enums.ExamFinishStatusEnum;
import com.qmth.jkserver.enums.RegistrationStatusEnum;
import com.qmth.jkserver.model.ExamPlan;
import com.qmth.jkserver.model.ExamRoom;
import com.qmth.jkserver.model.ExamStage;
import com.qmth.jkserver.model.ExamStudent;
import com.qmth.jkserver.service.*;
import com.qmth.jkserver.util.BlurryQueryUtil;
import com.qmth.jkserver.util.ServletUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description: 考试进度统计服务实现类
 * @Author: CaoZixuan
 * @Date: 2021-11-16
 */
@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class ExamProgressStatisticServiceImpl implements ExamProgressStatisticService {
    //    @Resource
//    private MongoTemplate mongoTemplate;

    @Resource
    @Lazy
    private JkExamPlanService examPlanService;

    @Resource
    JkExamStageService jkExamStageService;

    @Resource
    JkExamRoomService jkExamRoomService;

    @Resource
    @Lazy
    ExamStudentService examStudentService;

    @Resource
    ExamStudentDao examStudentDao;

    @Autowired
    JkCacheService cacheService;

    @Override
    public List<QueryResultDto> findExamConditionCascade(String examCode, String batchId, String stageId, String placeCode, String roomCode, ExamConditionEnum examConditionEnum) {
        List<QueryResultDto> queryResultDtoList = new ArrayList<>();
        if (Objects.isNull(examConditionEnum)) {
            throw new JkServerException("请选择要查询的查询条件");
        }
        Long schoolId = (Long) ServletUtil.getRequestHeaderSchoolId();
//        String token = ServletUtil.getRequest().getHeader(Constants.REQUEST_HEADER_TOKEN);
        switch (examConditionEnum) {
            case EXAM_PLAN:
                // 考试任务查询
//                List<ExamPlan> examPlanList = mongoTemplate.findAll(ExamPlan.class);
                QueryWrapper<ExamPlan> examPlanQueryWrapper = new QueryWrapper<>();
                if (Objects.nonNull(schoolId) && schoolId.longValue() > 0) {
                    examPlanQueryWrapper.lambda().eq(ExamPlan::getSchoolId, schoolId);
                }
//                User user = SystemConstant.tokenGetUser(token);
//                if (user != null && !user.getUserName().equals("super")) {
//                    examPlanQueryWrapper.lambda().eq(ExamPlan::getCreatorId, user.getId());
//                }
                examPlanQueryWrapper.lambda().orderByDesc(ExamPlan::getCreateTime);
                List<ExamPlan> examPlanList = examPlanService.list(examPlanQueryWrapper);
                queryResultDtoList = examPlanList.stream().flatMap(e -> Stream.of(new QueryResultDto(String.valueOf(e.getId()), e.getExamCode(), e.getName(), examConditionEnum)))
                        .collect(Collectors.toList());
                break;
            case EXAM_BATCH:
                // 考试批次查询 联动考试
//                Query batchQuery = new Query();
                QueryWrapper<ExamStage> examStageQueryWrapper = new QueryWrapper<>();
                if (StringUtils.isNotEmpty(examCode)) {
//                    batchQuery.addCriteria(Criteria.where("examCode").is(examCode));
                    examStageQueryWrapper.lambda().eq(ExamStage::getExamCode, examCode);
                }
//                List<ExamStage> examBachList = mongoTemplate.find(batchQuery, ExamStage.class);
                List<ExamStage> examBachList = jkExamStageService.list(examStageQueryWrapper);
                queryResultDtoList = examBachList.stream().flatMap(e -> {
                    Map<String, String> batchMap = new HashMap<>();
                    batchMap.put("batchId", e.getBatchId());
                    batchMap.put("batchName", e.getBatchName());
                    return Stream.of(batchMap);
                }).distinct().flatMap(e -> Stream.of(new QueryResultDto(e.get("batchId"), null, e.get("batchName"), examConditionEnum))).collect(Collectors.toList());
                break;
            case EXAM_STAGE:
                // 考试场次查询 联动考试 -> 批次
//                Query stageQuery = new Query();
                examStageQueryWrapper = new QueryWrapper<>();
                if (StringUtils.isNotEmpty(examCode)) {
//                    stageQuery.addCriteria(Criteria.where("examCode").is(examCode));
                    examStageQueryWrapper.lambda().eq(ExamStage::getExamCode, examCode);
                }
                if (StringUtils.isNotEmpty(batchId)) {
//                    stageQuery.addCriteria(Criteria.where("batchId").is(batchId));
                    examStageQueryWrapper.lambda().eq(ExamStage::getBatchId, batchId);
                }
//                List<ExamStage> examStageList = mongoTemplate.find(stageQuery, ExamStage.class);
                List<ExamStage> examStageList = jkExamStageService.list(examStageQueryWrapper);
                queryResultDtoList = examStageList.stream().flatMap(e -> {
                    Map<String, String> batchMap = new HashMap<>();
                    batchMap.put("stageId", e.getStageId());
                    batchMap.put("stageValue", new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(e.getStageStartTime()) + SystemConstant.HYPHEN + new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(e.getStageEndTime()));
                    return Stream.of(batchMap);
                }).distinct().flatMap(e -> Stream.of(new QueryResultDto(e.get("stageId"), null, e.get("stageValue"), examConditionEnum))).collect(Collectors.toList());
                break;
            case EXAM_PLACE:
                // 考试考点查询 联动考试
//                Query placeQuery = new Query();
                QueryWrapper<ExamRoom> examRoomQueryWrapper = new QueryWrapper<>();
                if (StringUtils.isNotEmpty(examCode)) {
//                    placeQuery.addCriteria(Criteria.where("examCode").is(examCode));
                    examRoomQueryWrapper.lambda().eq(ExamRoom::getExamCode, examCode);
                }
//                List<ExamRoom> examPlaceList = mongoTemplate.find(placeQuery, ExamRoom.class);
                List<ExamRoom> examPlaceList = jkExamRoomService.list(examRoomQueryWrapper);
                queryResultDtoList = examPlaceList.stream().flatMap(e -> {
                    Map<String, String> placeMap = new HashMap<>();
                    placeMap.put("placeCode", e.getPlaceCode());
                    placeMap.put("placeName", e.getPlaceName());
                    return Stream.of(placeMap);
                }).distinct().flatMap(e -> Stream.of(new QueryResultDto(null, e.get("placeCode"), e.get("placeName"), examConditionEnum))).collect(Collectors.toList());
                break;
            case EXAM_ROOM:
                // 考试考场查询 联动考试 -> 考点
//                Query roomQuery = new Query();
                examRoomQueryWrapper = new QueryWrapper<>();
                if (StringUtils.isNotEmpty(examCode)) {
//                    roomQuery.addCriteria(Criteria.where("examCode").is(examCode));
                    examRoomQueryWrapper.lambda().eq(ExamRoom::getExamCode, examCode);
                }
                if (StringUtils.isNotEmpty(batchId)) {
//                    roomQuery.addCriteria(Criteria.where("placeCode").is(placeCode));
                    examRoomQueryWrapper.lambda().eq(ExamRoom::getPlaceCode, placeCode);
                }
//                List<ExamRoom> examRoomList = mongoTemplate.find(roomQuery, ExamRoom.class);
                List<ExamRoom> examRoomList = jkExamRoomService.list(examRoomQueryWrapper);
                queryResultDtoList = examRoomList.stream().flatMap(e -> {
                    Map<String, String> roomMap = new HashMap<>();
                    roomMap.put("roomCode", e.getRoomCode());
                    roomMap.put("roomName", e.getRoomCode());
                    return Stream.of(roomMap);
                }).distinct().flatMap(e -> Stream.of(new QueryResultDto(null, e.get("roomCode"), e.get("roomName"), examConditionEnum))).collect(Collectors.toList());
                break;
            case EXAM_COURSE:
                // 考试课程查询 联动考试 -> 批次 -> 场次 -> 考点 -> 考场
//                Query courseQuery = new Query();
                QueryWrapper<ExamStudent> examStudentQueryWrapper = new QueryWrapper<>();
                if (StringUtils.isNotEmpty(examCode)) {
//                    courseQuery.addCriteria(Criteria.where("examCode").is(examCode));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getExamCode, examCode);
                }
                // 批次条件
                if (StringUtils.isNotEmpty(batchId)) {
//                    courseQuery.addCriteria(Criteria.where("batchId").is(batchId));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getBatchId, batchId);
                }
                // 场次条件
                if (StringUtils.isNotEmpty(stageId)) {
//                    courseQuery.addCriteria(Criteria.where("stageId").is(stageId));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getStageId, stageId);
                }
                // 考点条件
                if (StringUtils.isNotEmpty(placeCode)) {
//                    courseQuery.addCriteria(Criteria.where("placeCode").is(placeCode));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getPlaceCode, placeCode);
                }
                // 考场条件
                if (StringUtils.isNotEmpty(roomCode)) {
//                    courseQuery.addCriteria(Criteria.where("roomCode").is(roomCode));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getRoomCode, roomCode);
                }
//                List<ExamStudent> examStudentList = mongoTemplate.find(courseQuery, ExamStudent.class);
                List<ExamStudent> examStudentList = examStudentService.list(examStudentQueryWrapper);
                queryResultDtoList = examStudentList.stream().flatMap(e -> {
                    Map<String, String> courseMap = new HashMap<>();
                    courseMap.put("courseCode", e.getCourseCode());
                    courseMap.put("courseName", e.getCourseName());
                    return Stream.of(courseMap);
                }).distinct().flatMap(e -> Stream.of(new QueryResultDto(null, e.get("courseCode"), e.get("courseName"), examConditionEnum))).collect(Collectors.toList());
                break;
            case EXAM_FINISH_STATUS:
                queryResultDtoList = Arrays.stream(ExamFinishStatusEnum.values())
                        .flatMap(e -> Stream.of(new QueryResultDto(null, e.name(), e.getValue(), examConditionEnum)))
                        .collect(Collectors.toList());
                break;
            case REGISTRATION_STATUS:
                queryResultDtoList = Arrays.stream(RegistrationStatusEnum.values())
                        .flatMap(e -> Stream.of(new QueryResultDto(null, e.name(), e.getDesc(), examConditionEnum)))
                        .collect(Collectors.toList());
                break;
            default:
                break;
        }
        return queryResultDtoList;
    }

    @Override
    public ExamProgressStatisticDto findExamProgressStatisticByExamCode(String examCode) {
        ExamProgressStatisticDto result = new ExamProgressStatisticDto();
        if (StringUtils.isNotEmpty(examCode)) {
//            Query examStudentQuery = new Query();
//            examStudentQuery.addCriteria(Criteria.where("examCode").is(examCode));
            QueryWrapper<ExamStudent> examStudentQueryWrapper = new QueryWrapper<>();
            examStudentQueryWrapper.lambda().eq(ExamStudent::getExamCode, examCode);
            // 该考试任务下的考生
//            List<ExamStudent> examStudentList = mongoTemplate.find(examStudentQuery, ExamStudent.class);
            List<ExamStudent> examStudentList = examStudentService.list(examStudentQueryWrapper);

            // 应考科次
            int totalTimes = examStudentList.size();
            // 实考科次
            int realityTimes = (int) examStudentList.stream().filter(e -> ExamFinishStatusEnum.FINISHED.equals(e.getExamFinishStatus())).count();
            // 完成率
            BigDecimal finishRate = BigDecimal.ZERO;
            if (totalTimes > 0) {
                finishRate = BigDecimal.valueOf(realityTimes).divide(BigDecimal.valueOf(totalTimes), 2, RoundingMode.HALF_UP);
            }
            // 已检录科次
            List<ExamStudent> registrationExamStudentList = examStudentList.stream().filter(e -> e.getRegistrationStatus() != null).collect(Collectors.toList());
            int registrationTotalTimes = registrationExamStudentList.size();
            // 检录通过科次
            int registrationPassTimes = (int) registrationExamStudentList.stream().filter(ExamStudent::getRegistrationStatus).count();
            // 检录不通过科次
            int registrationNoPassTimes = (int) registrationExamStudentList.stream().filter(e -> !e.getRegistrationStatus()).count();
            if (registrationTotalTimes != registrationPassTimes + registrationNoPassTimes) {
                throw new JkServerException("检录数据异常");
            }
            result = new ExamProgressStatisticDto(totalTimes, realityTimes, finishRate.multiply(new BigDecimal(100)),
                    registrationTotalTimes, registrationPassTimes, registrationNoPassTimes, 0, 0);
        }
        return result;
    }

    @Override
    public Page<ExamProgressDto> findExamProgressPage(String examCode, String batchId, String stageId, String placeCode,
                                                      String roomCode, String courseCode, RegistrationStatusEnum registrationStatus,
                                                      ExamFinishStatusEnum examFinishStatus, String examStudentParams, int pageNumber, int pageSize) {

//        Query query = new Query();
        QueryWrapper<ExamStudent> examStudentQueryWrapper = new QueryWrapper<>();
        // 考试任务条件
        if (StringUtils.isNotEmpty(examCode)) {
//            query.addCriteria(Criteria.where("examCode").is(examCode));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getExamCode, examCode);
        }
        // 批次条件
        if (StringUtils.isNotEmpty(batchId)) {
//            query.addCriteria(Criteria.where("batchId").is(batchId));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getBatchId, batchId);
        }
        // 场次条件
        if (StringUtils.isNotEmpty(stageId)) {
//            query.addCriteria(Criteria.where("stageId").is(stageId));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getStageId, stageId);
        }
        // 考点条件
        if (StringUtils.isNotEmpty(placeCode)) {
//            query.addCriteria(Criteria.where("placeCode").is(placeCode));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getPlaceCode, placeCode);
        }
        // 考场条件
        if (StringUtils.isNotEmpty(roomCode)) {
//            query.addCriteria(Criteria.where("roomCode").is(roomCode));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getRoomCode, roomCode);
        }
        // 科目条件
        if (StringUtils.isNotEmpty(courseCode)) {
//            query.addCriteria(Criteria.where("courseCode").is(courseCode));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getCourseCode, courseCode);
        }
        // 检录状态
        if (registrationStatus != null) {
//            query.addCriteria(Criteria.where("registrationStatus").is(registrationStatus.getDbValue()));
            examStudentQueryWrapper.lambda().eq(ExamStudent::getRegistrationStatus, registrationStatus.getDbValue());
        }
        // 考试完成状态
        if (examFinishStatus != null) {
            switch (examFinishStatus) {
                case FINISHED:
//                    query.addCriteria(Criteria.where("examFinishStatus").is(examFinishStatus));
                    examStudentQueryWrapper.lambda().eq(ExamStudent::getExamFinishStatus, examFinishStatus);
                    break;
                case UN_FINISHED:
//                    query.addCriteria(new Criteria().orOperator(Criteria.where("examFinishStatus").is(examFinishStatus), Criteria.where("examFinishStatus").is(null)));
                    examStudentQueryWrapper.lambda().and(i -> i.eq(ExamStudent::getExamFinishStatus, examFinishStatus)
                            .or().isNull(ExamStudent::getExamFinishStatus));
                    break;
                default:
                    break;
            }
        }
        // 关键字查询
        Pattern pattern = null;
        if (StringUtils.isNotEmpty(examStudentParams)) {
            //模糊匹配
            pattern = BlurryQueryUtil.buildBlurryElement(examStudentParams, BlurryQueryTypeEnum.FUZZY_MATCHING);
//            query.addCriteria(new Criteria().orOperator(Criteria.where("name").regex(pattern), Criteria.where("ticketNo").regex(pattern)));
            Pattern finalPattern = pattern;
            examStudentQueryWrapper.lambda().and(i -> i.like(ExamStudent::getName, finalPattern)
                    .or().like(ExamStudent::getTicketNo, finalPattern));
        }
        // 统计查询结果数量
//        long count = mongoTemplate.count(query, ExamStudent.class);
//        long count = examStudentService.count(examStudentQueryWrapper);
        // 排序 考点 -》 考场 -》 课程编号 -》 准考证号
//        Sort.Order order1 = new Sort.Order(Sort.Direction.ASC, "placeCode");
//        Sort.Order order2 = new Sort.Order(Sort.Direction.ASC, "roomCode");
//        Sort.Order order3 = new Sort.Order(Sort.Direction.ASC, "courseCode");
//        Sort.Order order4 = new Sort.Order(Sort.Direction.ASC, "ticketNo");
//        Sort sort = new Sort(order1, order2, order3, order4);
//        query.with(sort);
//        query.limit(pageSize);
//        query.skip((pageNumber - 1) * pageSize);
        // 考生结果
//        List<ExamStudent> examStudentList = mongoTemplate.find(query, ExamStudent.class);
        Long schoolId = (Long) ServletUtil.getRequestHeaderSchoolId();
        if (Objects.nonNull(schoolId) && schoolId.longValue() < 0) {
            schoolId = null;
        }
        IPage<ExamStudent> examStudentList = examStudentDao.findByIpage(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNumber, pageSize), schoolId,
                examCode, batchId, stageId, placeCode, roomCode, courseCode,
                Objects.nonNull(registrationStatus) ? registrationStatus.getDbValue() : null,
                Objects.nonNull(examFinishStatus) ? examFinishStatus.name() : null,
                Objects.nonNull(pattern) ? pattern.pattern() : null);
        Map<String, ExamPlan> examPlanMap = new HashMap<>();
        Map<String, ExamRoom> examRoomMap = new HashMap<>();
        List<ExamProgressDto> examProgressDtoList = new ArrayList<>();

        for (ExamStudent examStudent : examStudentList.getRecords()) {
            String dbExamCode = examStudent.getExamCode();
            String dbPlaceCode = examStudent.getPlaceCode();
            String dbRoomCode = examStudent.getRoomCode();
            // 考试任务 - （考试编号 - 考试名称组）
            String dbExamName;
            if (examPlanMap.containsKey(dbExamCode)) {
                dbExamName = examPlanMap.get(dbExamCode).getName();
            } else {
                ExamPlan dbExamPlan = examPlanService.findByExamCode(dbExamCode);
                if (Objects.isNull(dbExamPlan)) {
                    throw new JkServerException("未找到【考试任务编号】：" + dbExamCode + "的考试任务");
                }
                dbExamName = dbExamPlan.getName();
                examPlanMap.put(dbExamCode, dbExamPlan);
            }
            // 考场
            String examRoomIdx = examCode + SystemConstant.JOINT_MARK + dbPlaceCode + SystemConstant.JOINT_MARK + dbRoomCode;
            String dbPlaceName;
            String dbRoomName;
            if (examRoomMap.containsKey(examRoomIdx)) {
                ExamRoom examRoom = examRoomMap.get(examRoomIdx);
                dbPlaceName = examRoom.getPlaceName();
                dbRoomName = examRoom.getRoomName();
            } else {
//                Query examRoomQuery = new Query();
//                examRoomQuery.addCriteria(Criteria.where("examCode").is(dbExamCode));
//                examRoomQuery.addCriteria(Criteria.where("placeCode").is(dbPlaceCode));
//                examRoomQuery.addCriteria(Criteria.where("roomCode").is(dbRoomCode));
//                ExamRoom dbExamRoom = mongoTemplate.findOne(examRoomQuery, ExamRoom.class);
                QueryWrapper<ExamRoom> examRoomQueryWrapper = new QueryWrapper<>();
                examRoomQueryWrapper.lambda().eq(ExamRoom::getExamCode, dbExamCode)
                        .eq(ExamRoom::getPlaceCode, dbPlaceCode)
                        .eq(ExamRoom::getRoomCode, dbRoomCode);
                ExamRoom dbExamRoom = jkExamRoomService.getOne(examRoomQueryWrapper);
                if (Objects.isNull(dbExamRoom)) {
                    throw new JkServerException("未找到【考试任务编号】 ： " + dbExamCode + ",【考点编号】 ： " + dbPlaceCode + ",【考场编号】 ： " + dbRoomCode + "的考场信息");
                }
                dbPlaceName = dbExamRoom.getPlaceName();
                dbRoomName = dbExamRoom.getRoomName();
                examRoomMap.put(examRoomIdx, dbExamRoom);
            }


            ExamProgressDto cell = new ExamProgressDto();
            cell.setExamCode(dbExamCode);
            cell.setExamName(dbExamName);
            cell.setBatchName(examStudent.getBatchName());
            cell.setStageId(examStudent.getStageId());
            cell.setPlaceCode(examStudent.getPlaceCode());
            cell.setPlaceName(dbPlaceName);
            cell.setRoomCode(examStudent.getRoomCode());
            cell.setRoomName(dbRoomName);
            cell.setExamStudentName(examStudent.getName());
            cell.setExamStudentTicketNo(examStudent.getTicketNo());
            cell.setCourseCode(examStudent.getCourseCode());
            cell.setCourseName(examStudent.getCourseName());
            cell.setRegistrationStatus(RegistrationStatusEnum.convertIdToName(examStudent.getRegistrationStatus()));

            ExamFinishStatusEnum examFinishStatusEnum = examStudent.getExamFinishStatus();
            cell.setExamStatus(examFinishStatusEnum != null ? examFinishStatusEnum.getValue() : null);
            examProgressDtoList.add(cell);
        }
        return new PageImpl<>(examProgressDtoList, PageRequest.of(pageNumber - 1, pageSize), examStudentList.getTotal());
    }
}
