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

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
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.admin.OpenExamBean;
import com.qmth.themis.business.bean.admin.OpenExamCourseBean;
import com.qmth.themis.business.bean.admin.OpenRecordAnswerTempBean;
import com.qmth.themis.business.bean.admin.OpenRecordNeedMarkBean;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.cache.bean.ExamPaperCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.response.TEExamStudentDto;
import com.qmth.themis.business.entity.TEExam;
import com.qmth.themis.business.enums.InvigilateMonitorStatusEnum;
import com.qmth.themis.business.enums.RecordSelectStrategyEnum;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.OssUtil;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class TEOpenServiceImpl implements TEOpenService {

    private final static Logger log = LoggerFactory.getLogger(TEOpenServiceImpl.class);

    @Resource
    TEExamService examService;

    @Resource
    TEExamCourseService examCourseService;

    @Resource
    TEExamPaperService examPaperService;

    @Resource
    TOeExamRecordService examRecordService;

    @Resource
    TOeExamAnswerService examAnswerService;

    @Resource
    TEExamStudentService examStudentService;

    @Resource
    OssUtil ossUtil;

    @Override
    public List<OpenExamBean> examQueryPage(Long examId, String examCode, int pageNumber, int pageSize) {
        Page<OpenExamBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.desc("t.id"));
        IPage<OpenExamBean> ret = examService.examQueryForOpen(ipage, examId, examCode);
        return ret.getRecords();
    }

    @Override
    public List<OpenExamCourseBean> examCourseQueryPage(Long examId, String courseCode, Boolean hasPaper,
                                                        int pageNumber, int pageSize) {
        checkExam(examId);
        Page<OpenExamCourseBean> ipage = new Page<>(pageNumber, pageSize);
        ipage.addOrder(OrderItem.desc("t.id"));
        IPage<OpenExamCourseBean> ret = examCourseService.examCourseQueryForOpen(ipage, examId, courseCode, hasPaper);
        return ret.getRecords();
    }

    @Override
    public JSONObject examPaperDetail(Long paperId, String filter) throws IOException {
        ExamPaperCacheBean paper = examPaperService.getExamPaperCacheBean(paperId);
        if (paper == null) {
            throw new BusinessException("未找到试卷信息");
        }
        checkExam(paper.getExamId());
        if (StringUtils.isBlank(paper.getAnswerPath())) {
            throw new BusinessException("该试卷未上传答案");
        }
        boolean acceptObjective = filter == null || "objective".equals(filter);
        boolean acceptSubjective = filter == null || "subjective".equals(filter);
        //解析答案JSON文件
        JSONObject answerJson = JSONObject
                .parseObject(new String(ossUtil.download(false, paper.getAnswerPath()), StandardCharsets.UTF_8));
        JSONArray answerDetails = answerJson.getJSONArray(SystemConstant.DETAILS);
        //解析试卷JSON文件
        JSONObject paperResult = JSONObject
                .parseObject(new String(ossUtil.download(false, paper.getPaperViewPath()), StandardCharsets.UTF_8));
        paperResult.put(SystemConstant.ID, paper.getId());
        paperResult.put("code", paper.getCode());
        paperResult.put(SystemConstant.NAME, paper.getName());
        JSONArray detailCollection = new JSONArray();
        JSONArray paperDetails = paperResult.getJSONArray(SystemConstant.DETAILS);
        for (int i = 0; i < paperDetails.size(); i++) {
            //遍历所有大题
            JSONObject paperDetail = paperDetails.getJSONObject(i);
            JSONObject answerDetail = SystemConstant.findJsonObject(answerDetails, paperDetail.getIntValue(SystemConstant.NUMBER));
            //按条件过滤需要的小题同时合并答案
            JSONArray questionCollection = SystemConstant.filterQuestions(paperDetail.getJSONArray(SystemConstant.QUESTIONS),
                    answerDetail != null ? answerDetail.getJSONArray(SystemConstant.QUESTIONS) : null, acceptObjective,
                    acceptSubjective);
            //有小题的情况下，本大题才需要被包含
            if (questionCollection.size() > 0) {
                paperDetail.put(SystemConstant.QUESTIONS, questionCollection);
                detailCollection.add(paperDetail);
            }
        }
        paperResult.put(SystemConstant.DETAILS, detailCollection);
        return paperResult;
    }

    private TEExam checkExam(Long examId) {
        ExamCacheBean examCacheBean = examService.getExamCacheBean(examId);
        TEExam exam = examService.cacheConvert(examCacheBean);
        if (exam == null) {
            throw new BusinessException("未找到考试信息");
        }
        if (exam.getEnable() == null || exam.getEnable().intValue() == 0) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ENABLE);
        }
        if (!InvigilateMonitorStatusEnum.FINISHED.equals(exam.getMonitorStatus())) {
            throw new BusinessException("考试批次监考未结束");
        }
        return exam;
    }

    @Override
    public JSONArray examRecordNeedMark(Long examId, String courseCode, Long idGt, Integer count) {
        JSONArray result = new JSONArray();
        TEExam exam = checkExam(examId);
        Map<Long, ExamPaperCacheBean> paperMap = new HashMap<>();
        final Map<Long, Map<String, Integer>> paperStructMap = new HashMap<>();
        //查询需要参与评卷的考生
        List<TEExamStudentDto> examStudents = examStudentService
                .findExamStudentNeedMark(examId, courseCode, idGt, count);
        if (examStudents != null) {
            for (TEExamStudentDto examStudent : examStudents) {
                JSONObject obj = new JSONObject();
                obj.put(SystemConstant.ID, examStudent.getId());
                obj.put("identity", examStudent.getIdentity());
                obj.put(SystemConstant.NAME, examStudent.getName());
                obj.put("activityCode", examStudent.getActivityCode());
                obj.put(SystemConstant.COURSE_CODE, examStudent.getCourseCode());
                obj.put(SystemConstant.COURSE_NAME, examStudent.getCourseName());
                //查询需要主观题评卷的考试记录
                List<OpenRecordNeedMarkBean> recordList = findExamRecordNeedMark(examStudent.getId(), paperMap,
                        exam.getRecordSelectStrategy());
                for (OpenRecordNeedMarkBean record : recordList) {
                    //查询所有单题作答结果
                    List<OpenRecordAnswerTempBean> answersTemp = examAnswerService.findByExamRecordId(record.getId());
                    record = SystemConstant.filterAnswer(record, answersTemp, paperStructMap);
                }
                obj.put("records", recordList);
                result.add(obj);
            }
        }
        paperMap.clear();
        paperStructMap.clear();
        return result;
    }

    private List<OpenRecordNeedMarkBean> findExamRecordNeedMark(Long examStudentId,
                                                                Map<Long, ExamPaperCacheBean> paperMap, RecordSelectStrategyEnum strategy) {
        List<OpenRecordNeedMarkBean> list = examRecordService.findExamRecordNeedMark(examStudentId);
        List<OpenRecordNeedMarkBean> result = new ArrayList<>();
        if (list == null) {
            return result;
        }
        double highestObjectiveScore = 0.0;
        long maxFinishTime = 0L;
        boolean subjective = false;
        for (OpenRecordNeedMarkBean bean : list) {
            //获取对应的试卷信息
            ExamPaperCacheBean paper = paperMap.get(bean.getPaperId());
            if (paper == null) {
                paper = examPaperService.getExamPaperCacheBean(bean.getPaperId());
                if (paper == null) {
                    log.error("找不到对应的试卷信息, paperId=" + bean.getPaperId());
                    continue;
                } else {
                    paperMap.put(bean.getPaperId(), paper);
                }
            }
            //根据试卷结构过滤没有主观题的情况
            if (paper.getTotalSubjectiveScore() == 0) {
                subjective = true;
                maxFinishTime = Math.max(maxFinishTime, bean.getFinishTime());
                continue;
            }
            highestObjectiveScore = Math.max(highestObjectiveScore, bean.getObjectiveScore());
            maxFinishTime = Math.max(maxFinishTime, bean.getFinishTime());
            result.add(bean);
        }
        if (result.isEmpty()) {
            return result;
        }
        switch (strategy) {
            case HIGHEST_OBJECTIVE_SCORE:
                if (subjective) {
                    final long time = maxFinishTime;
                    return list.stream().filter(bean -> bean.getFinishTime() == time).collect(Collectors.toList());
                } else {
                    //客观分最高，客观分相同则全部提交阅卷
                    final double score = highestObjectiveScore;
                    List<OpenRecordNeedMarkBean> finalResult = new ArrayList<>(result.size());
                    List<OpenRecordNeedMarkBean> openRecordNeedMarkBeanList = result.stream().filter(bean -> bean.getObjectiveScore() == score).collect(Collectors.toList());
                    if (openRecordNeedMarkBeanList.size() > 1) {
                        OpenRecordNeedMarkBean openRecordNeedMarkBean = openRecordNeedMarkBeanList.stream().max((p1, p2) -> Long.compare(p1.getFinishTime(), p2.getFinishTime())).get();
                        finalResult.add(openRecordNeedMarkBean);
                    } else {
                        finalResult.add(openRecordNeedMarkBeanList.get(0));
                    }
                    return finalResult;
                }
            case LATEST:
                //最后一次提交
                final long time = maxFinishTime;
                return result.stream().filter(bean -> bean.getFinishTime() == time).collect(Collectors.toList());
            default:
                //总分最高，全部提交阅卷
                return result;
        }
    }
}