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

import com.alibaba.fastjson.JSON;
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.distributed.print.business.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.ExamPaperStructure;
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.ExamPaperStructureService;
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.SysUser;
import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
import com.qmth.teachcloud.common.enums.QuestionType;
import com.qmth.teachcloud.common.util.ExcelUtil;
import com.qmth.teachcloud.common.util.ServletUtil;
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.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
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 ExamPaperStructureService examPaperStructureService;
    @Resource
    private ExamTaskService examTaskService;

    @Override
    public List<GradePaperStructResult> findGradePaperStructureResultList(String paperNumber, String paperType, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        List<GradePaperStructResult> result = new ArrayList<>();
        // 先从分析试卷结构表取
        List<GradePaperStructResult> gradeStructDatasource = this.baseMapper.findStructByGradePaper(schoolId, paperNumber, paperType);
        if (gradeStructDatasource != null && gradeStructDatasource.size() > 0) {
            result = gradeStructDatasource;
        } else {
            // 如果老师还没设置过分析参数 则从评卷参数中把试卷结构取出来
            ExamPaperStructure examPaperStructure = examPaperStructureService.getOne(new QueryWrapper<ExamPaperStructure>()
                    .lambda()
                    .eq(ExamPaperStructure::getSchoolId, schoolId)
                    .eq(ExamPaperStructure::getPaperNumber, paperNumber)
                    .eq(ExamPaperStructure::getPaperType, paperType));
            if (Objects.isNull(examPaperStructure)) {
                throw ExceptionResultEnum.ERROR.exception("该试卷还没有上传评卷参数设置");
            }
            ExamTask examTask = examTaskService.getOne(new QueryWrapper<ExamTask>().lambda()
                    .eq(ExamTask::getSchoolId, schoolId)
                    .eq(ExamTask::getPaperNumber, paperNumber));
            if (Objects.isNull(examTask)) {
                throw ExceptionResultEnum.ERROR.exception("没有找到试卷编号对应的命题任务");
            }
            String courseCode = examTask.getCourseCode();
            String courseName = examTask.getCourseName();

            // 客观题
            List<Question> examPaperObjList = new ArrayList<>();
            String examPaperObj = examPaperStructure.getObjectiveStructure();
            if (SystemConstant.strNotNull(examPaperObj)) {
                examPaperObjList = JSON.parseArray(examPaperObj, Question.class);
            }
            // 主观题
            List<Question> examPaperSubList = new ArrayList<>();
            String examPaperSub = examPaperStructure.getSubjectiveStructure();
            if (SystemConstant.strNotNull(examPaperSub)) {
                examPaperSubList = JSON.parseArray(examPaperSub, Question.class);
            }
            // 按题号排序
            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(QuestionType.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(QuestionType.SUBJECTIVE);
                cell.setBigTopicName(question.getMainTitle());
                cell.setBigQuestionNumber(String.valueOf(question.getMainNumber()));
                cell.setSmallQuestionNumber(question.getSubNumber());
                cell.setFullScore(question.getTotalScore());
                result.add(cell);
            }
        }
        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();
        List<GradePaperStructDatasource> datasource = gradePaperStructParam.getDatasource();
        // 验证批次分析试卷是否在计算
        gradeBatchPaperService.checkOperateAuth(schoolId, paperNumber, paperType);
        // 检验结构正确性并构建分析试卷结构
        List<GradePaperStruct> gradePaperStructList = this.checkAndBuildGradePaperStruct(datasource, paperNumber, paperType, paperName, requestUser);
        // 删除旧的试卷结构
        this.remove(new QueryWrapper<GradePaperStruct>()
                .lambda()
                .eq(GradePaperStruct::getSchoolId, schoolId)
                .eq(GradePaperStruct::getPaperNumber, paperNumber)
                .eq(GradePaperStruct::getPaperType, paperType));
        this.saveBatch(gradePaperStructList);
    }

    @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;
        });
        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(QuestionType.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(String paperNumber, String paperType, SysUser requestUser) 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(paperNumber, paperType, requestUser);
        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, String paperNumber, String paperType) {
        return this.baseMapper.findBySchoolIdAndPaperNumberAndPaperType(schoolId, paperNumber, paperType);
    }

    /**
     * 检验结构正确性并构建分析试卷结构
     *
     * @param datasource  数据源
     * @param paperNumber 试卷编号
     * @param paperType   试卷类型
     * @param paperName   试卷名称
     * @param requestUser 请求用户
     * @return 创建好的分析试卷结构
     */
    private List<GradePaperStruct> checkAndBuildGradePaperStruct(List<GradePaperStructDatasource> datasource, String paperNumber, String paperType, String paperName, SysUser requestUser) {
        Long schoolId = requestUser.getSchoolId();
        Long userId = requestUser.getId();
        ExamPaperStructure examPaperStructure = examPaperStructureService.getOne(new QueryWrapper<ExamPaperStructure>()
                .lambda()
                .eq(ExamPaperStructure::getSchoolId, schoolId)
                .eq(ExamPaperStructure::getPaperNumber, paperNumber)
                .eq(ExamPaperStructure::getPaperType, paperType));
        if (Objects.isNull(examPaperStructure)) {
            throw ExceptionResultEnum.ERROR.exception("该试卷还没有上传评卷参数设置");
        }

        // 客观题
        List<Question> examPaperObjList = new ArrayList<>();
        String examPaperObj = examPaperStructure.getObjectiveStructure();
        if (SystemConstant.strNotNull(examPaperObj)) {
            examPaperObjList = JSON.parseArray(examPaperObj, Question.class);
        }

        List<Question> gradePaperObjList = datasource.stream()
                .filter(e -> QuestionType.OBJECTIVE.equals(e.getNumberType()))
                .flatMap(e -> {
                    Question cell = new Question();
                    cell.setMainNumber(Integer.valueOf(e.getBigQuestionNumber()));
                    cell.setMainTitle(e.getBigTopicName());
                    cell.setSubNumber(e.getSmallQuestionNumber());
                    cell.setTotalScore(e.getFullScore());
                    return Stream.of(cell);
                })
                .collect(Collectors.toList());
        if (!Question.matchTwoQuestionList(examPaperObjList, gradePaperObjList)) {
            throw ExceptionResultEnum.ERROR.exception("试卷编号为【" + paperNumber + "】,试卷类型为【" + paperType +
                    "】的分析试卷结构和 评卷参数试卷结构在【" + QuestionType.OBJECTIVE.getDesc() + "】上不一致");
        }

        // 主观题
        List<Question> examPaperSubList = new ArrayList<>();
        String examPaperSub = examPaperStructure.getSubjectiveStructure();
        if (SystemConstant.strNotNull(examPaperSub)) {
            examPaperSubList = JSON.parseArray(examPaperSub, Question.class);
        }

        List<Question> gradePaperSubList = datasource.stream()
                .filter(e -> QuestionType.SUBJECTIVE.equals(e.getNumberType()))
                .flatMap(e -> {
                    Question cell = new Question();
                    cell.setMainNumber(Integer.valueOf(e.getBigQuestionNumber()));
                    cell.setMainTitle(e.getBigTopicName());
                    cell.setSubNumber(e.getSmallQuestionNumber());
                    cell.setTotalScore(e.getFullScore());
                    return Stream.of(cell);
                })
                .collect(Collectors.toList());
        if (!Question.matchTwoQuestionList(examPaperSubList, gradePaperSubList)) {
            throw ExceptionResultEnum.ERROR.exception("试卷编号为【" + paperNumber + "】,试卷类型为【" + paperType +
                    "】的分析试卷结构和 评卷参数试卷结构在【" + QuestionType.SUBJECTIVE.getDesc() + "】上不一致");
        }

        return datasource.stream().flatMap(e -> {
            GradePaperStruct gradePaperStruct = new GradePaperStruct();
            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());
    }
}
