package cn.com.qmth.examcloud.tool.service.export_student_answer_and_score_detail;

import cn.com.qmth.examcloud.tool.cache.LoginSessionManager;
import cn.com.qmth.examcloud.tool.config.SysProperty;
import cn.com.qmth.examcloud.tool.entity.TaskEntity;
import cn.com.qmth.examcloud.tool.service.CommonService;
import cn.com.qmth.examcloud.tool.service.export_student_answer_and_score_detail.vo.*;
import cn.com.qmth.examcloud.tool.utils.*;
import cn.com.qmth.examcloud.tool.vo.Pager;
import cn.com.qmth.examcloud.tool.vo.paper.QuestionType;
import cn.com.qmth.examcloud.tool.vo.user.User;
import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;

@Component
public class ExportStudentAnswerAndScoreDetailTask {

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

    @Autowired
    private CommonService commonService;

    @Autowired
    private SysProperty sysProperty;

    public void start(TaskEntity task) {
        JsonNode jsonParams = new JsonMapper().getNode(task.getParams());
        if (jsonParams == null) {
            throw new StatusException("任务参数解析错误！");
        }

        User user = LoginSessionManager.getLoginSession(task.getUserToken());
        if (user == null) {
            throw new StatusException("用户未登录！");
        }

        final String tempDir = "export_" + DateHelper.currentTimeMillis() + "_" + task.getId();
        FileHelper.makeDirs(sysProperty.getDataDir() + "/" + tempDir);

        this.execute(user, jsonParams, tempDir);

        // 压缩文件
        FileHelper.zip(new File(sysProperty.getDataDir() + "/" + tempDir),
                new File(sysProperty.getDataDir() + "/" + tempDir + ".zip"));
        task.setFilePath(tempDir + ".zip");
    }

    private void execute(User user, JsonNode jsonParams, String tempDir) {
        Long examId = jsonParams.get("examId").asLong(0L);
        String courseCodes = jsonParams.get("courseCodes").asText("");
        Boolean needSubjective = jsonParams.get("needSubjective").asBoolean(false);
        Boolean needQuestionId = jsonParams.get("needQuestionId").asBoolean(false);
        log.info("examId = {}, courseCodes = {}, needSubjective = {}, needQuestionId = {}", examId, courseCodes,
                needSubjective, needQuestionId);

        // 待处理的课程列表（未指定课程则按考试的全部课程）
        List<CourseVO> todoCourses;
        List<CourseVO> courses = commonService.getExamCourseList(user, examId);
        if (StringUtils.isNotBlank(courseCodes)) {
            todoCourses = new ArrayList<>();
            String[] courseCodeList = StringUtils.split(courseCodes, ",");
            for (String courseCode : courseCodeList) {
                for (CourseVO vo : courses) {
                    if (vo.getCourseCode().equals(courseCode)) {
                        todoCourses.add(vo);
                    }
                }
            }
        } else {
            todoCourses = courses;
        }
        log.info("examId:{}, 待处理的课程数为:{}", examId, todoCourses.size());

        int index = 0;
        for (CourseVO course : todoCourses) {
            // 按考试课程逐个处理
            this.export(user, examId, course, needSubjective, needQuestionId, tempDir);

            log.info("examId:{} courseId:{} 已处理课程数:{}", examId, course.getCourseId(), ++index);
        }
    }

    private void export(User user, Long examId, CourseVO course, Boolean needSubjective, Boolean needQuestionId,
            String tempDir) {
        List<ExamStudentScoreVO> examStudentScoreList = this.getExamStudentScoreList(user, examId,
                course.getCourseId());

        // Excel动态列
        List<String> dynamicExcelHeaders = new ArrayList<>();

        int index = 0;
        List<List<String>> excelRows = new ArrayList<>();
        for (ExamStudentScoreVO vo : examStudentScoreList) {
            index++;
            if (index % 500 == 0) {
                float rate = index * 100f / examStudentScoreList.size();
                log.info("examId:{} courseId:{} 已获取考生作答明细数:{} 进度:{}%", examId, course.getCourseId(), index,
                        rate);
            }

            List<String> rowValues = Lists.newArrayList(vo.getOrgName(), vo.getCourseCode(), vo.getCourseName(),
                    vo.getCourseLevel(), vo.getIdentityNumber(), vo.getStudentCode(), vo.getStudentName(),
                    vo.getGrade(), vo.getSpecialtyName(), vo.getIsAbsent(), vo.getObjectiveScore().replace("--", "0"),
                    vo.getSubjectiveScore().replace("--", "0"), vo.getFinalExamScore().replace("--", "0"),
                    vo.getBasePaperId());
            excelRows.add(rowValues);

            if (vo.getExamRecordDataId() == null) {
                // 跳过缺考的情况
                // log.warn("【缺考】examId={} courseId={} identityNumber={} finished={} absent={}", examId, course.getCourseId(),
                //         vo.getIdentityNumber(), vo.getIsFinished(), vo.getIsAbsent());
                continue;
            }

            // 获取考试试题作答记录
            List<ExamQuestionVO> examQuestions = this.getExamRecordQuestions(user, vo.getExamRecordDataId());

            if (dynamicExcelHeaders.isEmpty()) {
                for (ExamQuestionVO question : examQuestions) {
                    if (!isObjectiveQuestion(question.getQuestionType()) && !needSubjective) {
                        // 不需要主观题
                        continue;
                    }

                    if (needQuestionId) {
                        dynamicExcelHeaders.add(String.format("%s%s-%s-ID", question.getQuestionType().getDesc(),
                                question.getMainNumber(), question.getOrder()));
                    }
                    dynamicExcelHeaders.add(
                            String.format("%s%s-%s答案", question.getQuestionType().getDesc(), question.getMainNumber(),
                                    question.getOrder()));
                    dynamicExcelHeaders.add(
                            String.format("%s%s-%s作答", question.getQuestionType().getDesc(), question.getMainNumber(),
                                    question.getOrder()));
                    dynamicExcelHeaders.add(
                            String.format("%s%s-%s得分", question.getQuestionType().getDesc(), question.getMainNumber(),
                                    question.getOrder()));
                }
            }

            boolean existSubjectiveQuestion = false;
            for (ExamQuestionVO question : examQuestions) {
                if (!isObjectiveQuestion(question.getQuestionType())) {
                    existSubjectiveQuestion = true;
                    break;
                }
            }

            Map<String, Double> subjectiveQuestionScores = null;
            if (existSubjectiveQuestion && needSubjective) {
                // 获取阅卷端考生考试记录中主观题的得分明细
                subjectiveQuestionScores = this.getSubjectiveQuestionScores(user, vo.getExamRecordDataId());
            }

            // Excel动态列值
            List<String> dynamicColumnValues = new ArrayList<>();
            for (ExamQuestionVO question : examQuestions) {
                if (isObjectiveQuestion(question.getQuestionType())) {
                    String studentAnswer, correctAnswer;
                    if (QuestionType.TRUE_OR_FALSE == question.getQuestionType()) {
                        studentAnswer = convertTrueFalse(question.getStudentAnswer());
                        correctAnswer = convertTrueFalse(question.getCorrectAnswer());
                    } else {
                        correctAnswer = numbersToLetters(question.getCorrectAnswer());
                        studentAnswer = numbersToLetters(question.getStudentAnswer());
                    }

                    if (needQuestionId) {
                        dynamicColumnValues.add(question.getQuestionId());
                    }
                    dynamicColumnValues.add(correctAnswer);
                    dynamicColumnValues.add(studentAnswer);
                    if (correctAnswer.equals(studentAnswer)) {
                        dynamicColumnValues.add(question.getQuestionScore() != null ?
                                String.valueOf(question.getQuestionScore()) :
                                "0");
                    } else {
                        dynamicColumnValues.add("0");
                    }
                } else {
                    if (needSubjective) {
                        if (needQuestionId) {
                            dynamicColumnValues.add(question.getQuestionId());
                        }
                        String correctAnswer = question.getCorrectAnswer() != null ? question.getCorrectAnswer() : "";
                        dynamicColumnValues.add(Jsoup.clean(correctAnswer, Safelist.simpleText()));
                        String studentAnswer = question.getStudentAnswer() != null ? question.getStudentAnswer() : "";
                        dynamicColumnValues.add(Jsoup.clean(studentAnswer, Safelist.simpleText()));

                        Double studentScore = subjectiveQuestionScores.get(
                                question.getMainNumber() + "-" + question.getOrder());
                        dynamicColumnValues.add(studentScore != null ? String.valueOf(studentScore) : "0");
                    }
                }
            }
            rowValues.addAll(dynamicColumnValues);
        }

        List<String> excelHeaders = Lists.newArrayList("学习中心", "课程代码", "课程名称", "课程层次", "身份证号",
                "学号", "姓名", "年级", "专业", "是否缺考", "客观总分", "主观总分", "总分", "试卷ID");
        excelHeaders.addAll(dynamicExcelHeaders);

        final String filePath =
                sysProperty.getDataDir() + "/" + tempDir + "/" + examId + "_" + course.getCourseCode() + ".xlsx";
        EasyExcel.write(filePath).head(ExcelHelper.buildHeaders(excelHeaders))
                .registerWriteHandler(ExcelHelper.sheetStrategy()).registerWriteHandler(ExcelHelper.styleStrategy())
                .sheet().doWrite(excelRows);

        log.info("examId:{} courseId:{} courseCode:{} totalRows:{} 已处理！", examId, course.getCourseId(),
                course.getCourseCode(), excelRows.size());

        excelRows.clear();
        excelHeaders.clear();
        examStudentScoreList.clear();
    }

    /**
     * 是否为客观题
     */
    private boolean isObjectiveQuestion(QuestionType type) {
        return QuestionType.SINGLE_CHOICE == type || QuestionType.MULTIPLE_CHOICE == type
                || QuestionType.TRUE_OR_FALSE == type;
    }

    /**
     * 获取阅卷端考生考试记录中主观题的得分明细
     */
    private Map<String, Double> getSubjectiveQuestionScores(User user, Long examRecordDataId) {
        Map<String, String> headers = new HashMap<>();
        headers.put("key", user.getKey());
        headers.put("token", user.getToken());

        String url =
                user.getServerUrl() + "/api/ecs_marking/student/paper/getSubjectiveQuestionScores/" + examRecordDataId;
        String json = HttpHelper.post(url, headers, null);

        JsonMapper jsonMapper = new JsonMapper();
        List<SubjectiveQuestionScoreVO> scores = jsonMapper.toList(json, SubjectiveQuestionScoreVO.class);

        Map<String, Double> subjectiveQuestionScores = new HashMap<>();
        if (CollectionUtils.isNotEmpty(scores)) {
            for (SubjectiveQuestionScoreVO vo : scores) {
                subjectiveQuestionScores.put(vo.getMainNumber() + "-" + vo.getOrders(), vo.getScore());
            }
        }
        return subjectiveQuestionScores;
    }

    /**
     * 获取考试试题作答记录
     */
    private List<ExamQuestionVO> getExamRecordQuestions(User user, Long examRecordDataId) {
        Map<String, String> params = new HashMap<>();
        params.put("examRecordDataId", String.valueOf(examRecordDataId));

        Map<String, String> headers = new HashMap<>();
        headers.put("key", user.getKey());
        headers.put("token", user.getToken());

        String url = user.getServerUrl() + "/api/ecs_oe_admin/examRecordQuestions/getExamRecordQuestions";
        String json = HttpHelper.get(url, headers, params);

        JsonMapper jsonMapper = new JsonMapper();
        ExamRecordQuestionsVO examRecordQuestions = jsonMapper.parseJson(json, ExamRecordQuestionsVO.class);
        if (examRecordQuestions == null || CollectionUtils.isEmpty(examRecordQuestions.getExamQuestionEntities())) {
            return new ArrayList<>();
        }

        return examRecordQuestions.getExamQuestionEntities();
    }

    /**
     * 获取考生成绩明细列表
     */
    private List<ExamStudentScoreVO> getExamStudentScoreList(User user, Long examId, Long courseId) {
        Map<String, Object> params = new HashMap<>();
        params.put("examId", examId);
        params.put("courseId", courseId);
        params.put("pageSize", 500);

        Map<String, String> headers = new HashMap<>();
        headers.put("key", user.getKey());
        headers.put("token", user.getToken());

        String url = user.getServerUrl() + "/api/ecs_oe_admin/exam/score/statistic/list/for/tool";
        List<ExamStudentScoreVO> all = new ArrayList<>();
        JsonMapper jsonMapper = new JsonMapper();

        int sum = 0, pageNo = 0;
        while (true) {
            params.put("pageNo", ++pageNo);
            String json = HttpHelper.post(url, headers, params);

            Pager<ExamStudentScoreVO> page = jsonMapper.parseJson(json, new TypeReference<Pager<ExamStudentScoreVO>>() {

            });
            if (page == null || CollectionUtils.isEmpty(page.getContent())) {
                break;
            }
            all.addAll(page.getContent());

            sum += page.getContent().size();
            float rate = sum * 100f / page.getTotalElements();
            log.info("examId:{} courseId:{} 已获取考生成绩明细数:{} 进度:{}%", examId, courseId, sum, rate);
        }

        return all;
    }

    private String numbersToLetters(String numberStr) {
        if (StringUtils.isEmpty(numberStr)) {
            return "";
        }

        List<Integer> numbers = new ArrayList<>();
        if (numberStr.startsWith("[") && numberStr.endsWith("]")) {
            // Json数据格式，如：[0,1,2,3,4,5,6,7,8,9,...,25]
            String[] values = numberStr.replace("[", "").replace("]", "").split(",");
            for (String value : values) {
                if (value.matches("^\\d+$")) {
                    numbers.add(Integer.parseInt(value.trim()));
                }
            }
        } else {
            // 旧数据格式，如：0123456789
            char[] chars = numberStr.toCharArray();
            for (char c : chars) {
                if (String.valueOf(c).matches("^\\d+$")) {
                    numbers.add(c - 48);
                }
            }
        }
        Collections.sort(numbers);

        List<String> letters = new ArrayList<>();
        for (int number : numbers) {
            if (number >= 0 && number <= 25) {
                // 只处理0-25之间的数字
                char c = (char) (65 + number);
                letters.add(String.valueOf(c));
            }
        }

        return StringUtils.join(letters, ",");
    }

    private String convertTrueFalse(String value) {
        if ("true".equals(value)) {
            return "对";
        } else if ("false".equals(value)) {
            return "错";
        } else if ("1".equals(value)) {
            return "对";
        } else if ("0".equals(value)) {
            return "错";
        } else {
            return "";
        }
    }

}
