package com.qmth.teachcloud.report.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.service.BasicCourseService;
import com.qmth.teachcloud.report.business.bean.dto.Answer;
import com.qmth.teachcloud.report.business.bean.params.CalculateParams;
import com.qmth.teachcloud.report.business.entity.*;
import com.qmth.teachcloud.report.business.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description:
 * @Author: CaoZixuan
 * @Date:
 */
@Service
public class AnalyzeDataCheckServiceImpl implements AnalyzeDataCheckService {
    @Resource
    private TBExamCourseService tbExamCourseService;
    @Resource
    private TBCommonLevelConfigService tbCommonLevelConfigService;
    @Resource
    private TBCommonRankLevelConfigService tbCommonRankLevelConfigService;
    @Resource
    private AnalyzeForReportService analyzeForReportService;
    @Resource
    private TBPaperService tbPaperService;
    @Resource
    private TBPaperStructService tbPaperStructService;
    @Resource
    private BasicCourseService basicCourseService;
    @Resource
    private AnalyzeDataGetAndEditService analyzeDataGetAndEditService;
    @Resource
    private TBExamRecordService tbExamRecordService;
    @Resource
    private TBAnswerService tbAnswerService;
    @Resource
    private TBExamStudentService tbExamStudentService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void checkPaperTotal(Long schoolId, Long examId, String courseCode) {
        TBPaper tbPaper = tbPaperService.getOne(new QueryWrapper<TBPaper>().lambda().eq(TBPaper::getExamId, examId).eq(TBPaper::getCourseCode, courseCode));
        if (Objects.isNull(tbPaper)) {
            throw ExceptionResultEnum.ERROR.exception("未找到试卷");
        }
        Long paperId = tbPaper.getId();
        BigDecimal totalScore = tbPaper.getTotalScore();

        List<TBPaperStruct> tbPaperStructList = tbPaperStructService.list(new QueryWrapper<TBPaperStruct>().lambda().eq(TBPaperStruct::getPaperId, paperId));
        BigDecimal sum = BigDecimal.ZERO;
        for (TBPaperStruct tbPaperStruct : tbPaperStructList) {
            sum = sum.add(tbPaperStruct.getFullScore());
        }
        if (totalScore.compareTo(sum) != 0) {
            throw ExceptionResultEnum.ERROR.exception("试卷总分和试卷结构中各个题目分数之和不一致");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void checkExamStudentAnswer(Long schoolId, Long examId, String courseCode) {
        TBPaper tbPaper = tbPaperService.getOne(new QueryWrapper<TBPaper>().lambda()
                .eq(TBPaper::getExamId,examId)
                .eq(TBPaper::getCourseCode,courseCode));
        if (Objects.isNull(tbPaper)){
            throw ExceptionResultEnum.ERROR.exception("未找到试卷信息");
        }
        Long paperId = tbPaper.getId();
        // 试卷结构蓝图
        List<TBPaperStruct> tbPaperStructList = tbPaperStructService.list(new QueryWrapper<TBPaperStruct>().lambda()
                .eq(TBPaperStruct::getPaperId,paperId));

        List<TBExamRecord> tbExamRecordList = tbExamRecordService.list(new QueryWrapper<TBExamRecord>().lambda().eq(TBExamRecord::getPaperId,paperId));
        if (tbExamRecordList.isEmpty()){
            throw ExceptionResultEnum.ERROR.exception("没有找到考生成绩记录");
        }
        List<Long> examStudentIdList = tbExamRecordList.stream().map(TBExamRecord::getExamStudentId).collect(Collectors.toList());

        // 考生(不缺考的)
        List<TBExamStudent> tbExamStudentList = tbExamStudentService.list(new QueryWrapper<TBExamStudent>()
                .lambda()
                .in(TBExamStudent::getId,examStudentIdList))
                .stream()
                .filter(e -> !e.getAbsent())
                .collect(Collectors.toList());
        List<Long> effectStudentIdList = tbExamStudentList.stream().map(TBExamStudent::getId).collect(Collectors.toList());
        int effectExamStudentCount = tbExamStudentList.size();
        // 有效的考生成绩记录
        List<TBExamRecord> effectTBExamRecordList = tbExamRecordList.stream().filter(e -> effectStudentIdList.contains(e.getExamStudentId())).collect(Collectors.toList());
        List<Long> effectRecordIdList = effectTBExamRecordList.stream().map(TBExamRecord::getId).collect(Collectors.toList());
        if (effectRecordIdList.isEmpty()){
            throw ExceptionResultEnum.ERROR.exception("缺少有效的考生");
        }

        // 作答
        // 1.考生人数对应检查 - 本地试卷结构数量 * 考生数 =？ 考生作答总记录数
        List<TBAnswer> tbAnswerList = tbAnswerService.list(new QueryWrapper<TBAnswer>().lambda().in(TBAnswer::getExamRecordId,effectRecordIdList));
        int answerCount = 0;
        Map<Integer,Long> recordAnswerCountCheckMap = new HashMap<>();
        Map<Long,List<TBAnswer>> recordAnswerMap = new HashMap<>();
        for (Long effectRecordId : effectRecordIdList) {
            List<TBAnswer> cell = tbAnswerList.stream().filter(e -> Objects.equals(e.getExamRecordId(), effectRecordId)).collect(Collectors.toList());
            recordAnswerMap.put(effectRecordId,cell);
            recordAnswerCountCheckMap.put(cell.size(),effectRecordId);
        }

        if (recordAnswerCountCheckMap.keySet().size() > 1){
            throw ExceptionResultEnum.ERROR.exception("同一个试卷不同考生作答记录数量不一致");
        }

        List<TBAnswer> effectAnswerList = recordAnswerMap.get(effectRecordIdList.get(0));
        // 2.试卷结构对应检查 - 本地的试卷结构和考生作答的结构中 题目类型，大题号，小题号是否对应
        List<Answer> al1 = effectAnswerList.stream().flatMap(e -> {
            Answer answer = new Answer();
            answer.setNumberType(e.getNumberType());
            answer.setMainNumber(e.getMainNumber());
            answer.setSubNumber(e.getSubNumber());
            return Stream.of(answer);
        }).collect(Collectors.toList());

        List<Answer> al2 = tbPaperStructList.stream().flatMap(e -> {
            Answer answer = new Answer();
            answer.setNumberType(e.getNumberType());
            answer.setMainNumber(e.getBigQuestionNumber());
            answer.setSubNumber(e.getSmallQuestionNumber());
            return Stream.of(answer);
        }).collect(Collectors.toList());
        if (!Answer.matchTwoAnswerList(al1,al2)){
            throw ExceptionResultEnum.ERROR.exception("考生作答中的试卷结构和试卷结构不一致（检查内容 ： 题目类型（主观题、客观题），大题号，小题号）");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importConfigData(Long schoolId, Long examId, String courseCode) {
        if (tbExamCourseService.verifyExamCourseCantRun(examId, schoolId, courseCode, null)) {
            throw ExceptionResultEnum.ERROR.exception("课程编号[" + courseCode + "]的课程分析数据已测试或发布，不能变更基础数据");
        }
        String courseName = basicCourseService.findByCourseCode(courseCode, schoolId).getName();
        tbCommonLevelConfigService.importLevelConfig(examId, courseCode, courseName);
        tbCommonRankLevelConfigService.importRankLevelConfig(examId, courseCode, courseName);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void triggerAssign(Long schoolId, Long examId, String courseCode) {
        analyzeForReportService.normalAssignScore(examId, schoolId, courseCode);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void analyzeDataCheck(CalculateParams calculateParams) throws IOException {
        Long examId = calculateParams.getExamId();
        Long schoolId = calculateParams.getSchoolId();
        List<String> courseCodeList = calculateParams.getCourseCode();
        List<String> needRepeatCourseCodeList = new ArrayList<>();
        for (String courseCode : courseCodeList) {
            // 新增
            if (analyzeDataGetAndEditService.dataGetAndEdit(examId, courseCode, schoolId)){
                // 如果要重算 则记录该课程
                needRepeatCourseCodeList.add(courseCode);
                // 检查
                this.checkPaperTotal(schoolId, examId, courseCode);
                this.checkExamStudentAnswer(schoolId, examId, courseCode);

                // 触发分析的前置操作
                this.importConfigData(schoolId, examId, courseCode);
                this.triggerAssign(schoolId, examId, courseCode);
            }
        }
        calculateParams.setRepeatCalculateCourseCodeList(needRepeatCourseCodeList);
    }
}
