package cn.com.qmth.stmms.admin.thread;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
import cn.com.qmth.stmms.biz.exam.model.ExamStudentPaper;
import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.model.ScoreRate;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentPaperService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.ScoreRateService;
import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
import cn.com.qmth.stmms.biz.utils.ScoreCalculateUtil;
import cn.com.qmth.stmms.biz.utils.ScoreInfo;
import cn.com.qmth.stmms.biz.utils.ScoreItem;
import cn.com.qmth.stmms.common.enums.ExamStatus;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

public class ScoreCalculateThread implements Runnable {

    protected static Logger log = LoggerFactory.getLogger(ScoreCalculateThread.class);

    private ExamStudentService studentService;

    private ExamStudentPaperService studentPaperService;

    private ExamService examService;

    private ExamQuestionService questionService;

    private ExamSubjectService subjectService;

    private ScoreRateService scoreRateService;

    private MarkLibraryService libraryService;

    private Exam exam;

    private Map<String, List<ExamQuestion>> objectiveMap;

    private Map<String, List<ExamQuestion>> subjectiveMap;

    private Map<String, Set<String>> campusNameMap;

    private Map<String, Double> scoreRateMap;

    public ScoreCalculateThread(Exam exam, ExamService examService, ExamStudentService studentService,
            ExamStudentPaperService studentPaperService, ExamQuestionService questionService,
            ExamSubjectService subjectService, ScoreRateService scoreRateService, MarkLibraryService libraryService) {
        this.exam = exam;
        this.examService = examService;
        this.studentService = studentService;
        this.studentPaperService = studentPaperService;
        this.questionService = questionService;
        this.subjectService = subjectService;
        this.scoreRateService = scoreRateService;
        this.libraryService = libraryService;
        this.objectiveMap = new HashMap<String, List<ExamQuestion>>();
        this.subjectiveMap = new HashMap<String, List<ExamQuestion>>();
        this.scoreRateMap = new HashMap<String, Double>();
        this.campusNameMap = new HashMap<String, Set<String>>();
    }

    @Override
    public void run() {
        log.info("start calculate for examId=" + exam.getId());
        try {
            int pageNumber = 1;
            int pageSize = 1000;
            List<ExamStudent> list = studentService.findByExamIdAndUpload(exam.getId(), true, pageNumber, pageSize);
            while (list != null && list.size() > 0) {
                for (ExamStudent student : list) {
                    calculate(student);
                }
                pageNumber++;
                list = studentService.findByExamIdAndUpload(exam.getId(), true, pageNumber, pageSize);
            }
            // 统计各科目下每题得分率,主观题评卷完成才统计
            long total = subjectService.sumLibraryCount(exam.getId(), 0, 0);
            long finish = libraryService.countByExamAndStatus(exam.getId(), LibraryStatus.MARKED);
            // int percent = total > 0 ? (int) (finish * 100.00 / total) : 0;
            if (finish >= total) {
                statistics();
            }

        } catch (Exception e) {
            log.error("calculate exception for examId=" + exam.getId(), e);
        } finally {
            exam.setStatus(ExamStatus.START);
            examService.save(exam);

            log.info("finish calculate for examId=" + exam.getId());
        }
    }

    private void statistics() {
        List<ExamSubject> examSubjects = subjectService.list(exam.getId());
        for (ExamSubject examSubject : examSubjects) {
            Set<String> campusNameList = campusNameMap.get(examSubject.getCode());
            if (campusNameList != null) {
                for (String campusName : campusNameList) {
                    List<ExamQuestion> questions = this.findQuestionList(examSubject.getCode(), false);
                    Long count = studentService.countByExamIdAndSubjectCodeAndCampus(exam.getId(),
                            examSubject.getCode(), campusName, true, false);
                    for (ExamQuestion examQuestion : questions) {
                        ScoreRate scoreRate = scoreRateService.findOne(exam.getId(), examSubject.getCode(), campusName,
                                false, examQuestion.getMainNumber(), examQuestion.getSubNumber());
                        if (scoreRate == null) {
                            scoreRate = new ScoreRate();
                            scoreRate.setExamId(exam.getId());
                            scoreRate.setCampusName(campusName);
                            scoreRate.setSubjectCode(examSubject.getCode());
                            scoreRate.setMainNumber(examQuestion.getMainNumber());
                            scoreRate.setSubNumber(examQuestion.getSubNumber());
                            scoreRate.setObjective(examQuestion.isObjective());
                            scoreRate.setMainTitle(examQuestion.getMainTitle());
                            scoreRate.setTotalScore(examQuestion.getTotalScore());
                        }
                        String key = getKey(examSubject.getCode(), campusName, examQuestion.getQuestionNumber());
                        BigDecimal bg = new BigDecimal(scoreRateMap.get(key) * 1.00 / count);
                        double avgScore = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
                        if (scoreRate.getAvgScore() == null || scoreRate.getAvgScore() != avgScore) {
                            scoreRate.setAvgScore(avgScore);
                        }
                        if (scoreRate.getTotalScore() != examQuestion.getTotalScore()) {
                            scoreRate.setTotalScore(examQuestion.getTotalScore());
                        }
                        scoreRateService.save(scoreRate);
                    }
                }
            }
        }
    }

    private void calculate(ExamStudent student) {
        ScoreCalculateUtil util = ScoreCalculateUtil.instance(student);
        ScoreInfo info = util.calculate(findQuestionList(student, true), null);

        student.setObjectiveScore(info.getObjectiveScore());
        student.setScoreList(info.getScoreList(), true);
        if (student.getSubjectiveScore() == null || StringUtils.isBlank(student.getSubjectiveScoreList())) {
            // 当考生的主观分或得分明细为空时，再尝试同步一次评卷任务的分数
            MarkLibrary markLibrary = libraryService.findByStudentId(student.getId());
            if (markLibrary != null && markLibrary.getStatus() == LibraryStatus.MARKED) {
                // 存在评卷记录且已完成评卷时，同步评卷分数与明细
                student.setSubjectiveScore(markLibrary.getMarkerScore());
                student.setScoreList(markLibrary.getScoreList(), false);
            }
        }
        studentService.save(student);

        // 主观题有分数明细才统计
        if (StringUtils.isNotBlank(student.getSubjectiveScoreList())) {
            statistics(student);
        }
    }

    private void statistics(ExamStudent student) {
        List<ScoreItem> items = student.getScoreList(false);
        List<ExamQuestion> list = findQuestionList(student.getSubjectCode(), false);
        if (!items.isEmpty() && list != null) {
            int count = list.size();
            if (items.size() != count) {
                return;
            }
            for (int i = 0; i < count; i++) {
                ScoreItem scoreItem = items.get(i);
                String key = getKey(student.getSubjectCode(), student.getCampusName(), list.get(i).getQuestionNumber());
                Double totalScore = scoreRateMap.get(key);
                if (totalScore == null) {
                    scoreRateMap.put(key, scoreItem.getScore());
                } else {
                    scoreRateMap.put(key, totalScore + scoreItem.getScore());
                }
            }

            Set<String> campusNames = campusNameMap.get(student.getSubjectCode());
            if (campusNames == null) {
                campusNames = new HashSet<>();
                campusNameMap.put(student.getSubjectCode(), campusNames);
            }
            campusNames.add(student.getCampusName());
        }
    }

    private String getKey(String subjectCode, String campusName, String questionNumber) {
        List<String> list = new ArrayList<String>();
        list.add(subjectCode);
        list.add(campusName);
        list.add(questionNumber);
        return StringUtils.join(list, "-");
    }

    private List<ExamQuestion> findQuestionList(ExamStudent student, boolean objective) {
        List<ExamQuestion> list = null;
        // 优先取考生专属试题结构
        ExamStudentPaper paper = studentPaperService.find(student.getExamId(), student.getExamNumber(), objective);
        if (paper != null) {
            list = paper.getQuestionList();
        }
        // 没有再取科目通用客观题结构
        if (list == null || list.isEmpty()) {
            list = findQuestionList(student.getSubjectCode(), objective);
        }
        return list;
    }

    private List<ExamQuestion> findQuestionList(String subjectCode, boolean objective) {
        if (objective) {
            List<ExamQuestion> list = objectiveMap.get(subjectCode);
            if (list == null) {
                list = questionService.findByExamAndSubjectAndObjective(exam.getId(), subjectCode, true);
                objectiveMap.put(subjectCode, list);
            }
            return list;
        } else {
            List<ExamQuestion> list = subjectiveMap.get(subjectCode);
            if (list == null) {
                list = questionService.findByExamAndSubjectAndObjective(exam.getId(), subjectCode, false);
                subjectiveMap.put(subjectCode, list);
            }
            return list;
        }
    }
}
