package com.qmth.distributed.print.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.qmth.distributed.print.business.bean.dto.excel.GradePaperStructDto;
import com.qmth.distributed.print.business.bean.dto.open.PaperStructure;
import com.qmth.teachcloud.common.bean.marking.Question;
import com.qmth.distributed.print.business.bean.params.analyze.GradePaperStructDatasource;
import com.qmth.distributed.print.business.bean.params.analyze.GradePaperStructParam;
import com.qmth.distributed.print.business.bean.result.analyze.GradePaperStructResult;
import com.qmth.distributed.print.business.entity.ExamTask;
import com.qmth.distributed.print.business.entity.GradePaperStruct;
import com.qmth.distributed.print.business.mapper.GradePaperStructMapper;
import com.qmth.distributed.print.business.service.ExamTaskService;
import com.qmth.distributed.print.business.service.GradeBatchPaperService;
import com.qmth.distributed.print.business.service.GradePaperStructService;
import com.qmth.teachcloud.common.contant.SystemConstant;
import com.qmth.teachcloud.common.entity.MarkQuestion;
import com.qmth.teachcloud.common.entity.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.ObjectiveType;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;

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;

/**
 * <p>
 * 分析-试卷结构（蓝图）表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2022-05-20
 */
@Service
public class GradePaperStructServiceImpl extends ServiceImpl<GradePaperStructMapper, GradePaperStruct> implements GradePaperStructService {
    @Resource
    private GradeBatchPaperService gradeBatchPaperService;
    @Resource
    private ExamTaskService examTaskService;

    @Override
    public List<GradePaperStructResult> findGradePaperStructureResultList(Long examId, String paperNumber, String paperType, SysUser requestUser, List<MarkQuestion> markQuestionList) {
        Long schoolId = requestUser.getSchoolId();
        List<GradePaperStructResult> result = new ArrayList<>();
        // 先从分析试卷结构表取
        List<GradePaperStructResult> gradeStructDatasource = this.baseMapper.findStructByGradePaper(schoolId, examId, paperNumber, paperType);
        if (gradeStructDatasource != null && gradeStructDatasource.size() > 0) {
            // 如果已经设置了试卷结构查询之前配置好的分析参数-试卷结构
            result = gradeStructDatasource;
        } else {
            if (CollectionUtils.isEmpty(markQuestionList)) {
                throw ExceptionResultEnum.ERROR.exception("知学知考试卷结构不存在");
            }

            // 如果云阅卷同步回来的结构不为空，优先使用云阅卷的
            Map<ObjectiveType, List<Question>> examCloudPaperStructMap = parseExamCloudPaperStruct(markQuestionList);
            List<Question> examPaperObjList = examCloudPaperStructMap.get(ObjectiveType.OBJECTIVE);
            List<Question> examPaperSubList = examCloudPaperStructMap.get(ObjectiveType.SUBJECTIVE);

            List<ExamTask> examTaskList = examTaskService.list(new QueryWrapper<ExamTask>().lambda()
                    .eq(ExamTask::getSchoolId, schoolId)
                    .eq(ExamTask::getExamId, examId)
                    .eq(ExamTask::getPaperNumber, paperNumber));
            if (Objects.isNull(examTaskList)) {
                throw ExceptionResultEnum.ERROR.exception("没有找到试卷编号对应的命题任务");
            }
            if (examTaskList.size() > 1) {
                throw ExceptionResultEnum.ERROR.exception("有多个考试任务异常");
            }
            ExamTask examTask = examTaskList.get(0);
            String courseCode = examTask.getCourseCode();
            String courseName = examTask.getCourseName();

            // 按题号排序
            examPaperObjList = examPaperObjList.stream().sorted(Comparator.comparing(Question::getMainNumber).thenComparing(Question::getSubNumber)).collect(Collectors.toList());
            examPaperSubList = examPaperSubList.stream().sorted(Comparator.comparing(Question::getMainNumber).thenComparing(Question::getSubNumber)).collect(Collectors.toList());
            for (Question question : examPaperObjList) {
                GradePaperStructResult cell = new GradePaperStructResult();
                cell.setCourseCode(courseCode);
                cell.setCourseName(courseName);
                cell.setPaperNumber(paperNumber);
                cell.setPaperType(paperType);
                cell.setNumberType(ObjectiveType.OBJECTIVE);
                cell.setBigTopicName(question.getMainTitle());
                cell.setBigQuestionNumber(String.valueOf(question.getMainNumber()));
                cell.setSmallQuestionNumber(question.getSubNumber());
                cell.setFullScore(question.getTotalScore());
                result.add(cell);
            }

            for (Question question : examPaperSubList) {
                GradePaperStructResult cell = new GradePaperStructResult();
                cell.setCourseCode(courseCode);
                cell.setCourseName(courseName);
                cell.setPaperNumber(paperNumber);
                cell.setPaperType(paperType);
                cell.setNumberType(ObjectiveType.SUBJECTIVE);
                cell.setBigTopicName(question.getMainTitle());
                cell.setBigQuestionNumber(String.valueOf(question.getMainNumber()));
                cell.setSmallQuestionNumber(question.getSubNumber());
                cell.setFullScore(question.getTotalScore());
                result.add(cell);
            }
        }
        Comparator<GradePaperStructResult> bigComparator = Comparator.comparing(o -> Integer.parseInt(o.getBigQuestionNumber()));
        Comparator<GradePaperStructResult> smallComparator = Comparator.comparing(o -> Integer.parseInt(o.getSmallQuestionNumber()));
        result.sort(bigComparator.thenComparing(smallComparator));
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveGradePaperStructBatch(GradePaperStructParam gradePaperStructParam, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        String paperNumber = gradePaperStructParam.getPaperNumber();
        String paperType = gradePaperStructParam.getPaperType();
        String paperName = gradePaperStructParam.getPaperName();
        Long examId = gradePaperStructParam.getExamId();
        List<GradePaperStructDatasource> datasource = gradePaperStructParam.getDatasource();

        // 验证批次分析试卷是否在计算
        gradeBatchPaperService.checkOperateAuth(schoolId, examId, paperNumber, paperType);
        // 检验结构正确性并构建分析试卷结构
        List<GradePaperStruct> gradePaperStructList = this.checkAndBuildGradePaperStruct(datasource, examId, paperNumber, paperType, paperName, requestUser);
        // 删除旧的试卷结构
        this.remove(new QueryWrapper<GradePaperStruct>()
                .lambda()
                .eq(GradePaperStruct::getSchoolId, schoolId)
                .eq(GradePaperStruct::getExamId, examId)
                .eq(GradePaperStruct::getPaperNumber, paperNumber)
                .eq(GradePaperStruct::getPaperType, paperType));
        this.saveBatch(gradePaperStructList);
        gradeBatchPaperService.updatePaperAndBatchStatus(schoolId, examId, paperNumber, paperType);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importGradePaperStruct(MultipartFile file, String paperNumber, String paperType, String paperName, SysUser requestUser) throws IOException, NoSuchFieldException {
        if (Objects.isNull(file)) {
            throw ExceptionResultEnum.ERROR.exception("找不到附件");
        }
        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(GradePaperStructDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
            if (finalExcelErrorList.size() > 0) {
                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
            }
            return finalExcelList;
        }, 1);
        GradePaperStructParam gradePaperStructParam = new GradePaperStructParam();
        gradePaperStructParam.setPaperNumber(paperNumber);
        gradePaperStructParam.setPaperType(paperType);
        gradePaperStructParam.setPaperName(paperName);
        List<GradePaperStructDatasource> gradePaperStructDatasourceList = new ArrayList<>();

        if (Objects.nonNull(finalList) && finalList.size() > 0) {
            for (int i = 0; i < finalList.size(); i++) {
                LinkedMultiValueMap<Integer, Object> map = finalList.get(i);
                List<Object> importList = map.get(i);
                for (int y = 0; y < Objects.requireNonNull(importList).size(); y++) {
                    if (importList.get(y) instanceof GradePaperStructDto) {
                        GradePaperStructDto gradePaperStructDto = (GradePaperStructDto) importList.get(y);

                        GradePaperStructDatasource gradePaperStructDatasource = new GradePaperStructDatasource();
                        gradePaperStructDatasource.setBigQuestionNumber(gradePaperStructDto.getBigQuestionNumber());
                        gradePaperStructDatasource.setSmallQuestionNumber(gradePaperStructDto.getSmallQuestionNumber());
                        gradePaperStructDatasource.setBigTopicName(gradePaperStructDto.getBigTopicName());
                        gradePaperStructDatasource.setNumberType(ObjectiveType.convertToEnum(gradePaperStructDto.getNumberType()));
                        gradePaperStructDatasource.setFullScore(BigDecimal.valueOf(Double.parseDouble(gradePaperStructDto.getFullScore())));
                        gradePaperStructDatasource.setKnowledgeDimension(gradePaperStructDto.getKnowledgeDimension());
                        gradePaperStructDatasource.setAbilityDimension(gradePaperStructDto.getAbilityDimension());
                        gradePaperStructDatasourceList.add(gradePaperStructDatasource);
                    }
                }
            }
        }
        gradePaperStructParam.setDatasource(gradePaperStructDatasourceList);
        this.saveGradePaperStructBatch(gradePaperStructParam, requestUser);
    }

    @Override
    public void exportGradePaperStructTemplate(Long examId, String paperNumber, String paperType, @NotNull SysUser requestUser, List<MarkQuestion> markQuestionList) throws Exception {

        ExamTask examTask = examTaskService.getOne(new QueryWrapper<ExamTask>().lambda()
                .eq(ExamTask::getSchoolId, requestUser.getSchoolId())
                .eq(ExamTask::getPaperNumber, paperNumber));
        if (Objects.isNull(examTask)) {
            throw ExceptionResultEnum.ERROR.exception("没有找到试卷编号对应的命题任务");
        }
        String courseCode = examTask.getCourseCode();
        String courseName = examTask.getCourseName();

        List<GradePaperStructResult> datasource = this.findGradePaperStructureResultList(examId, paperNumber, paperType, requestUser, markQuestionList);
        List<GradePaperStructDto> gradePaperStructDtoList = datasource.stream().flatMap(e -> {
            GradePaperStructDto cell = new GradePaperStructDto();
            cell.setCourseCode(courseCode);
            cell.setCourseName(courseName);
            cell.setPaperNumber(e.getPaperNumber());
            cell.setPaperType(e.getPaperType());
            cell.setNumberType(e.getNumberType().getDesc());
            cell.setBigTopicName(e.getBigTopicName());
            cell.setBigQuestionNumber(e.getBigQuestionNumber());
            cell.setSmallQuestionNumber(e.getSmallQuestionNumber());
            cell.setFullScore(String.valueOf(e.getFullScore()));
            cell.setKnowledgeDimension(e.getKnowledgeDimension());
            cell.setAbilityDimension(e.getAbilityDimension());
            return Stream.of(cell);
        }).collect(Collectors.toList());
        ExcelUtil.excelExport(courseName + "-试卷蓝图导入模板", GradePaperStructDto.class, gradePaperStructDtoList, ServletUtil.getResponse());
    }

    @Override
    public List<PaperStructure> findBySchoolIdAndPaperNumberAndPaperType(Long schoolId, Long examId, String paperNumber, String paperType) {
        return this.baseMapper.findBySchoolIdAndPaperNumberAndPaperType(schoolId, examId, paperNumber, paperType);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateExamCloudPaperStruct(Long schoolId, String paperNumber, String paperType, List<MarkQuestion> markQuestionList) {
        // 更新试卷结构参数的云阅卷试卷结构
        if (CollectionUtils.isEmpty(markQuestionList)) {
            throw ExceptionResultEnum.ERROR.exception("知学知考试卷结构异常");
        }
        // 如果没有客观题 试卷类型传空
        paperType = null;
        // todo 3.3.0待更新
//        String cloudInfoJson = cloudMarkingTaskUtils.queryPaperStructure(schoolId, examId, subjectCode, paperType);
//        examPaperStructure.setCloudInfoJson(cloudInfoJson);
//        examPaperStructure.setStructureChange(false);
//        examPaperStructureService.updateById(examPaperStructure);

        // 更新grade_paper_struct
        QueryWrapper<GradePaperStruct> willDeleteQueryWrapper = new QueryWrapper<>();
        willDeleteQueryWrapper.lambda().eq(GradePaperStruct::getSchoolId, schoolId).eq(GradePaperStruct::getPaperNumber, paperNumber);
        if (SystemConstant.strNotNull(paperType)) {
            willDeleteQueryWrapper.lambda().eq(GradePaperStruct::getPaperType, paperType);
        }
        this.remove(willDeleteQueryWrapper);
    }

    /**
     * 检验结构正确性并构建分析试卷结构
     *
     * @param datasource  数据源
     * @param paperNumber 试卷编号
     * @param paperType   试卷类型
     * @param paperName   试卷名称
     * @param requestUser 请求用户
     * @return 创建好的分析试卷结构
     */
    private List<GradePaperStruct> checkAndBuildGradePaperStruct(List<GradePaperStructDatasource> datasource, Long examId, String paperNumber, String paperType, String paperName, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        Long userId = requestUser.getId();

        return datasource.stream().flatMap(e -> {
            GradePaperStruct gradePaperStruct = new GradePaperStruct();
            gradePaperStruct.setExamId(examId);
            gradePaperStruct.setSchoolId(schoolId);
            gradePaperStruct.setPaperNumber(paperNumber);
            gradePaperStruct.setPaperType(paperType);
            gradePaperStruct.setPaperName(paperName);
            gradePaperStruct.setQuestionName(e.getBigTopicName() + " " + e.getBigQuestionNumber() + SystemConstant.HYPHEN + e.getSmallQuestionNumber());
            gradePaperStruct.setNumberType(e.getNumberType());
            gradePaperStruct.setBigQuestionNumber(e.getBigQuestionNumber());
            gradePaperStruct.setSmallQuestionNumber(e.getSmallQuestionNumber());
            gradePaperStruct.setBigTopicName(e.getBigTopicName());
            gradePaperStruct.setFullScore(e.getFullScore());
            gradePaperStruct.setScoreRules(e.getScoreRules());
            if (SystemConstant.strNotNull(e.getKnowledgeDimension())) {
                gradePaperStruct.setKnowledgeDimension(e.getKnowledgeDimension());
            }
            if (SystemConstant.strNotNull(e.getAbilityDimension())) {
                gradePaperStruct.setAbilityDimension(e.getAbilityDimension());
            }
            gradePaperStruct.insertInfo(userId);
            return Stream.of(gradePaperStruct);
        }).collect(Collectors.toList());
    }

    private Map<ObjectiveType, List<Question>> parseExamCloudPaperStruct(List<MarkQuestion> markQuestionList) {
        Map<ObjectiveType, List<Question>> result = new HashMap<>();
        List<Question> objList = new ArrayList<>();
        List<Question> subList = new ArrayList<>();
        for (MarkQuestion markQuestion : markQuestionList) {
            boolean objective = markQuestion.getObjective();
            String mainTitle = markQuestion.getMainTitle();
            Integer mainNumber = markQuestion.getMainNumber();
            Integer subNumber = markQuestion.getSubNumber();
            double totalScore = markQuestion.getTotalScore();
            String answer = markQuestion.getAnswer();

            Question question = new Question();
            question.setMainTitle(mainTitle);
            question.setMainNumber(mainNumber);
            question.setSubNumber(String.valueOf(subNumber));
            question.setTotalScore(BigDecimal.valueOf(totalScore));
            question.setAnswer(answer);
            if (objective) {
                objList.add(question);
            } else {
                subList.add(question);
            }
        }
        result.put(ObjectiveType.OBJECTIVE, objList);
        result.put(ObjectiveType.SUBJECTIVE, subList);
        return result;
    }
}
