package com.qmth.jkserver.core.ow;

import com.qmth.jkserver.constant.SpringContextHolder;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.ApiException;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.dto.ExamStudentAnswerSorter;
import com.qmth.jkserver.enums.ExamStatus;
import com.qmth.jkserver.enums.ExamType;
import com.qmth.jkserver.model.*;
import com.qmth.jkserver.service.*;
import com.qmth.jkserver.util.FileStoreUtil;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 优特阅卷数据导出
 *
 * @author tianjun
 */
public class Utest2OWExportBackUp implements IOW2Export {

    private static final Logger LOGGER = LoggerFactory.getLogger(Utest2OWExport.class);

    @Override
    public File execute(String examCode) throws Exception {
        // 1\校验当前考试计划的状态是否符合导出的条件
        JkExamPlanService examPlanService = SpringContextHolder.getBean(JkExamPlanService.class);
        FileStoreUtil fileStoreConfig = SpringContextHolder.getBean(FileStoreUtil.class);
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (examPlan == null) {
            throw JkServerException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw JkServerException.EXAM_PLAN_UN_PUBLISHED;
        }
        if (!ExamType.UTEST.equals(examPlan.getExamType())) {
            throw new JkServerException("考试类型不是优特机考的类型");
        }
        ExamStudentAdditionalService examStudentAdditionalService = SpringContextHolder.getBean(ExamStudentAdditionalService.class);
        ExamStudentAnswerService examStudentAnswerService = SpringContextHolder.getBean(ExamStudentAnswerService.class);
        ExamStudentService examStudentService = SpringContextHolder.getBean(ExamStudentService.class);
        //缓存答案信息
        Map<String, ExamStudentAnswer> ANSWER_CAHCE = new HashMap<String, ExamStudentAnswer>();
        List<ExamStudentAnswer> allAnswers = examStudentAnswerService.findByExamCode(examCode, null);
        //缓存每一个科目的最大客观题体量
        Map<String, Integer> QUESTION_CACHE = new HashMap<>();
        for (ExamStudentAnswer answer : allAnswers) {
            ExamStudent examStudent = examStudentService.getById(answer.getStudentId());
            if (Objects.nonNull(examStudent)) {
                String key = examCode + examStudent.getTicketNo() + examStudent.getCourseCode();
                ANSWER_CAHCE.put(key, answer);
                if (!answer.getIsAbsent()) {
                    String objectAnswer = answer.getObjectiveAnswer();
                    if (StringUtils.isNotEmpty(objectAnswer)) {
                        String[] answers = StringUtils.splitByWholeSeparatorPreserveAllTokens(objectAnswer, ";");
                        String h = examCode + examStudent.getCourseCode();
                        Integer old = QUESTION_CACHE.get(h) == null ? 0 : QUESTION_CACHE.get(h);
                        QUESTION_CACHE.put(h, Math.max(old, answers.length));
                    }
                }
            }
        }
        //缓存所有的附加信息
        Map<String, ExamStudentAdditional> ADDITIONAL_CAHCE = new HashMap<String, ExamStudentAdditional>();
        Map<String, ExamStudentAdditional> ADDITIONAL_BASEINFO_CAHCE = new HashMap<String, ExamStudentAdditional>();
        List<String> examDistinctStudents = new ArrayList<String>();
        List<ExamStudentAdditional> allAdditions = examStudentAdditionalService.findByExamCode(examCode);
        for (ExamStudentAdditional additional : allAdditions) {
            String key = examCode + additional.getStudentId() + additional.getCourseCode();
            ADDITIONAL_CAHCE.put(key, additional);
            ADDITIONAL_BASEINFO_CAHCE.put(examCode + additional.getStudentId(), additional);
        }
//        List<BasicDBObject> basicDBObjects = examStudentAdditionalService.findDistinctStudent(examCode);
//        for (BasicDBObject object : basicDBObjects) {
        List<Map> basicDBObjects = examStudentAdditionalService.findDistinctStudent(examCode);
        for (Map object : basicDBObjects) {
            String ticketNo = (String) object.get("studentId");
            examDistinctStudents.add(ticketNo);
        }
        //缓存所有的考生信息
        Map<String, ExamStudent> STUDENT_CAHCE = new HashMap<String, ExamStudent>();
        List<ExamStudent> allStudents = examStudentService.findByExamCode(examCode);
        for (ExamStudent student : allStudents) {
            String key = examCode + student.getTicketNo() + student.getCourseCode();
            STUDENT_CAHCE.put(key, student);
        }
        //缓存所有的批次信息
        Map<String, ExamStage> STAGE_CAHCE = new HashMap<String, ExamStage>();
        List<ExamStage> allStages = examPlanService.findStageByExamCode(examCode);
        for (ExamStage examStage : allStages) {
            STAGE_CAHCE.put(examCode + examStage.getBatchId(), examStage);
        }
        //此次考试的所有科目
        Map<String, String> COURSE_CACHE = examStudentAdditionalService.distinctCourse(examCode);
        File dir = new File(fileStoreConfig.getWorkRoot());
        dir = new File(dir, new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        dir = new File(dir, SystemConstant.getNanoId());
        if (!dir.exists()) {
            dir.mkdirs();
            if (!dir.exists()) {
                throw JkServerException.withFieldValidateError("创建目录失败:" + dir.getAbsolutePath());
            }
            LOGGER.info("创建OW临时文件夹成功：" + dir.getAbsolutePath());
        }
        String timestamp = new SimpleDateFormat("yyyyy-MM-ddHHmmss").format(new Date());
        File zipFile = new File(dir, "OW阅卷数据_" + examCode + "_" + timestamp + SystemConstant.ZIP_PREFIX);
        zipFile.createNewFile();
        LOGGER.info("创建OW临时文件成功：" + zipFile.getAbsolutePath());
        OutputStream zipOutPutstarm = new FileOutputStream(zipFile);
        ZipOutputStream zipOutputStream = new ZipOutputStream(zipOutPutstarm, Charset.forName(SystemConstant.CHARSET_NAME));
        File storedir = new File(dir, "src");
        if (!storedir.exists()) {
            storedir.mkdirs();
        }
        String fileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "score.csv";
        File scoreFile = new File(storedir, fileName);
        scoreFile.createNewFile();
        //创建表头
        List<String> headers = buildHeaders(examCode, QUESTION_CACHE, COURSE_CACHE);
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < headers.size(); i++) {
            String header = headers.get(i);
            if (StringUtils.isEmpty(header)) {
                throw new JkServerException("表头创建错误");
            }
            stringBuffer.append(header).append(",");
        }
        stringBuffer.append("\r\n");//换行符号
        //填写数据
        int rowNum = 1;
        for (String ticketNo : examDistinctStudents) {
            //前面13列考生的基本信息
            ExamStudentAdditional additional = ADDITIONAL_BASEINFO_CAHCE.get(examCode + ticketNo);
            ExamStage examStage = STAGE_CAHCE.get(examCode + additional.getData().get("batchid"));
            if (examStage == null || examStage.getBatchStartTime() == null) {
                throw new JkServerException("批次考试时间为空,请重新检查已经导入的考务数据是否正确");
            }
//            Date batchStartTime = examStage.getBatchStartTime();
            Long batchStartTime = examStage.getBatchStartTime();
            String time = new SimpleDateFormat("yyyyMMdd").format(new Date(batchStartTime));
            stringBuffer.append(time).append(",");
            stringBuffer.append(String.valueOf(rowNum)).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("pro"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("county"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("school"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("grade"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("class"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("name"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("address"))).append(",");
            if (Objects.nonNull(additional.getStudentId())) {
                stringBuffer.append(additional.getStudentId()).append(",");
            }
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("sex"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("学科总成绩"))).append(",");
            stringBuffer.append(StringUtils.trimToEmpty(additional.getData().get("年级排名"))).append(",");
            //填写考生科目成绩 --汇总工具导出的考生科目原始成绩
            Map<String, Boolean> ykCache = new HashMap<>();//当前遍历考生是否应考某科目
            for (String key : COURSE_CACHE.keySet()) {
                //是否应考
                ExamStudentAdditional yk = ADDITIONAL_CAHCE.get(examCode + ticketNo + key);
                if (yk == null) {
                    stringBuffer.append(",");
                    ykCache.put(key, false);
                } else {
                    stringBuffer.append(yk.getData().get("成绩")).append(",");
                    ykCache.put(key, true);
                }
            }
            for (String key : COURSE_CACHE.keySet()) {
                //获取这个科目一共有多少题目
                Integer m = QUESTION_CACHE.get(examCode + key);
                if (m == null || m == 0) {
                    continue;
                }
                //如果不是应该考科目直接跳过
                if (!ykCache.get(key)) {
                    //1\试卷名称
                    stringBuffer.append(",");
                    //2\跳过Q TIME PATH
                    for (int p = 0; p < m * 3; p++) {
                        stringBuffer.append(",");
                    }
                } else {
                    //1\写入试卷名称
                    ExamStudent student = STUDENT_CAHCE.get(examCode + ticketNo + key);
                    if (student == null) {
                        throw new JkServerException("无法在student.csv中获取此条考务信息,考试识别码:" + examCode + ",准考证号:" + ticketNo + ",科目编码:" + key);
                    }
                    ExamStudentAnswer answer = ANSWER_CAHCE.get(examCode + ticketNo + key);
                    if (answer == null || Boolean.valueOf(answer.getIsAbsent())) {
                        if (answer == null) {
                            LOGGER.info("没有上传答案数据,考试识别码:" + examCode + ",准考证号:" + ticketNo + ",科目编码:" + key);
                            stringBuffer.append(",");
                        }
                        if (answer != null && Boolean.valueOf(answer.getIsAbsent())) {
                            LOGGER.info("发现考生缺考,考试识别码:" + examCode + ",准考证号:" + ticketNo + ",科目编码:" + key);
                            stringBuffer.append(",");
                        }
                    } else {
                        stringBuffer.append(StringUtils.trimToEmpty(answer.getPaperName())).append(",");
                    }
                    //2\写入Q TIME PATH
                    if (answer == null || Boolean.valueOf(answer.getIsAbsent()) || StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                        for (int i = 0; i < m; i++) {//作答
                            if (answer == null) {//不应考或者应考但是又没有把答案传给数据中心，则不填入任何数据
                                stringBuffer.append("O,");
                            } else {
                                if (answer.getIsAbsent()) {
                                    stringBuffer.append("#").append(",");
                                } else {
                                    if (StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                                        stringBuffer.append("O").append(",");
                                    }
                                }
                            }
                        }
                        for (int i = 0; i < m; i++) {//答题时间
                            if (answer == null) {
                                stringBuffer.append("O,");
                            } else {
                                if (answer.getIsAbsent()) {
                                    stringBuffer.append("O").append(",");
                                } else {
                                    if (StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                                        stringBuffer.append("O").append(",");
                                    }
                                }

                            }
                        }
                        for (int i = 0; i < m; i++) {//答题轨迹为空
                            if (answer == null) {
                                stringBuffer.append("O,");
                            } else {
                                if (answer.getIsAbsent()) {
                                    stringBuffer.append("O").append(",");
                                } else {
                                    if (StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                                        stringBuffer.append("O").append(",");
                                    }
                                }
                            }
                        }
                    } else {
                        String[] obs = StringUtils.splitByWholeSeparatorPreserveAllTokens(answer.getObjectiveAnswer(), ";");
                        if (obs == null) {
                            throw new JkServerException("客观题答案格式错误:" + answer.getObjectiveAnswer());
                        }
                        if (obs.length != m) {
                            throw new JkServerException("客观题答案题量不一致");
                        }
                        String[] times = StringUtils.splitByWholeSeparatorPreserveAllTokens(answer.getAnswerTime(), ";");
                        if (times.length != m) {
                            throw new JkServerException("答题时间题量不一致");
                        }
                        String[] paths = StringUtils.splitByWholeSeparatorPreserveAllTokens(answer.getAnswerPath(), ";");
                        if (paths.length != m) {
                            throw new JkServerException("答题轨迹题量不一致");
                        }
                        for (int i = 0; i < m; i++) {//作答
                            if (obs[i].equals("#")) {
                                stringBuffer.append("O").append(",");
                            } else {
                                if (StringUtils.isEmpty(obs[i])) {
                                    stringBuffer.append("O,");
                                } else {
                                    stringBuffer.append(obs[i]).append(",");
                                }
                            }
                        }
                        for (int i = 0; i < m; i++) {//答题时间
                            if (StringUtils.isEmpty(times[i])) {
                                stringBuffer.append("O,");
                            } else {
                                stringBuffer.append(times[i]).append(",");
                            }
                        }
                        for (int i = 0; i < m; i++) {//答题轨迹
                            if (StringUtils.isEmpty(paths[i])) {
                                stringBuffer.append("O,");
                            } else {
                                stringBuffer.append(paths[i]).append(",");
                            }
                        }
                    }
                }
            }
            stringBuffer.append("\r\n");//换行符号
            rowNum++;
        }
        BufferedWriter bufferedWriter = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(scoreFile);
            fileOutputStream.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
            OutputStreamWriter out = new OutputStreamWriter(fileOutputStream, SystemConstant.CHARSET_NAME);
            bufferedWriter = new BufferedWriter(out);
            bufferedWriter.write(stringBuffer.toString());
            bufferedWriter.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "Utest答案汇总文件生成错误");
        } finally {
            if (fileOutputStream != null) {
                IOUtils.closeQuietly(fileOutputStream);
            }
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
        }
        InputStream inputStream = null;
        try {
            LOGGER.info("@create Utest Score File success!:" + scoreFile.getAbsolutePath());
            inputStream = new FileInputStream(scoreFile);
            zipOutputStream.putNextEntry(new ZipEntry(scoreFile.getName()));
            IOUtils.copy(inputStream, zipOutputStream);
            zipOutputStream.closeEntry();
        } catch (Exception e) {
            LOGGER.error("error", e);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
        }
        try {
            buildObjectiveAnswerFile(examPlan, storedir, zipOutputStream);
            zipOutputStream.close();
        } finally {
            if (zipOutPutstarm != null) {
                IOUtils.closeQuietly(zipOutPutstarm);
            }
        }
        return zipFile;
    }

    protected void buildObjectiveAnswerFile(ExamPlan examPlan, File dir, ZipOutputStream zipOutputStream) throws IOException {
        ExamStudentAnswerService examStudentAnswerService = SpringContextHolder.getBean(ExamStudentAnswerService.class);
        ExamStudentService examStudentService = SpringContextHolder.getBean(ExamStudentService.class);
        List<ExamStudentAnswer> examStudentAnswers = examStudentAnswerService.findObjectiveAnswerByExamCode(examPlan.getExamCode());
        System.out.println("[buildObjectiveAnswerFile] start sort...:" + System.currentTimeMillis());
        Collections.sort(examStudentAnswers, new ExamStudentAnswerSorter());
        System.out.println("[buildObjectiveAnswerFile] end sort...:" + System.currentTimeMillis());
        File file = new File(dir, "ow_omr.txt");
        file.createNewFile();
        BufferedWriter bufferedWriter = null;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
//            CommonCacheService commonCacheService = SpringContextHolder.getBean(CommonCacheService.class);
//            SysConfig sysConfig = commonCacheService.addSysConfigCache(SystemConstant.SYS_CONFIG_KEY_CHARSETS);
            OutputStreamWriter outBatch = new OutputStreamWriter(fileOutputStream, SystemConstant.CHARSET_NAME);
            bufferedWriter = new BufferedWriter(outBatch);
            for (ExamStudentAnswer answer : examStudentAnswers) {
                if (answer == null || StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                    continue;
                }
                if (answer.getIsAbsent()) {
                    continue;
                }
                ExamStudent examStudent = examStudentService.getById(answer.getStudentId());
//                ExamStudent examStudent = answer.getStudent();
                if (StringUtils.isEmpty(examStudent.getTicketNo())) {
                    throw new JkServerException("缺少准考证号,考生ID=" + examStudent.getId());
                }
                if (StringUtils.isEmpty(answer.getPaperId())) {
                    throw new JkServerException("缺少试卷ID,考生ID=" + examStudent.getId());
                }
                StringBuffer stringBuffer = new StringBuffer();
                stringBuffer.append(examStudent.getTicketNo()).append(",");
                stringBuffer.append(answer.getPaperId()).append(",");
                stringBuffer.append(answer.getObjectiveAnswer()).append(",");
                stringBuffer.append("1");
                stringBuffer.append("\r\n");
                bufferedWriter.write(stringBuffer.toString());
            }
            bufferedWriter.flush();
        } finally {
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
        }
        InputStream inputStream = new FileInputStream(file);
        try {
            zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
            IOUtils.copy(inputStream, zipOutputStream);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
            zipOutputStream.closeEntry();
        }
    }

    protected List<String> buildHeaders(String examCode, Map<String, Integer> QUESTION_CACHE
            , Map<String, String> COURSE_CACHE) {
        List<String> headers = new ArrayList<String>();
        headers.add("time");
        headers.add("seq1");
        headers.add("pro");
        headers.add("country");
        headers.add("school");
        headers.add("grade");
        headers.add("class");
        headers.add("name");
        headers.add("address");
        headers.add("studentId");
        headers.add("sex");
        headers.add("学科总成绩");
        headers.add("年级排名");
        Map<String, String> map = COURSE_CACHE;
        //科目成绩
        for (String key : map.keySet()) {
            String courseName = map.get(key);
            headers.add(courseName + "(" + key + ")" + "成绩");
        }
        //科目成绩+formId,题目答案,答题时间,答题轨迹
        for (String key : map.keySet()) {
            String courseName = map.get(key);
            headers.add(courseName + "(" + key + ")" + "formId");
            headers = buildQuestionTimePathHeaders(examCode, key, headers, QUESTION_CACHE);
        }
        return headers;
    }

    protected List<String> buildQuestionTimePathHeaders(String examCode, String courseCode, List<String> hreades,
                                                        Map<String, Integer> QUESTION_CACHE) {
        Integer max = QUESTION_CACHE.get(examCode + courseCode);
        if (max == null || max == 0) {
            return hreades;
        }
        for (int i = 0; i < max; i++) {//客观题答案表头
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("Q").append(i + 1);
            hreades.add(stringBuffer.toString());
        }
        for (int j = 0; j < max; j++) {//答题时间表头
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("TIME").append(j + 1);
            hreades.add(stringBuffer.toString());
        }
        for (int n = 0; n < max; n++) {    //答题轨迹表头
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append("PATH").append(n + 1);
            hreades.add(stringBuffer.toString());
        }
        return hreades;
    }

}
