package com.qmth.jkserver.core.ow;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.jkserver.constant.SpringContextHolder;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.enums.ExamStatus;
import com.qmth.jkserver.enums.ExamType;
import com.qmth.jkserver.enums.SceneScore;
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 org.springframework.util.LinkedMultiValueMap;

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

/**
 * 通用机考阅卷数据导出
 *
 * @author tianjun
 */
public class Normal2OWExport implements IOW2Export {


    private static final Logger LOGGER = LoggerFactory.getLogger(Normal2OWExport.class);
    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public File execute(String examCode) throws IOException {
        //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.NORMAL.equals(examPlan.getExamType())) {
            throw new JkServerException("考试类型不是通用机考的考试类型");
        }
        File dir = new File(fileStoreConfig.getWorkRoot());
        dir = new File(dir, new SimpleDateFormat("yyyy-MM-dd").format(new Date()) + File.separator + SystemConstant.getNanoId());
        File storedir = new File(dir, "src");
        if (!storedir.exists()) {
            storedir.mkdirs();
        }
        LOGGER.info("创建OW临时文件夹成功：", dir.getAbsolutePath());
        String time = new SimpleDateFormat("yyyyy-MM-ddHHmmss").format(new Date());
        File zipFile = new File(dir, "OW阅卷数据_" + examCode + "_" + time + SystemConstant.ZIP_PREFIX);
        zipFile.createNewFile();
        OutputStream outputStream = new FileOutputStream(zipFile);
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream, Charset.forName(SystemConstant.CHARSET_NAME));
        try {
            //2\导出客观题答案文件ow_omr.txt文件
            buildObjectiveAnswerFile(examPlan, storedir, zipOutputStream);
            //3\导出主观题答案文件
            buildSubjectiveFile(examPlan, storedir, zipOutputStream);
            //4\导出考生试卷表
            buildStudentPaperFile(examPlan, storedir, zipOutputStream);
        } finally {
            if (zipOutputStream != null) {
                IOUtils.closeQuietly(zipOutputStream);
            }
            if (outputStream != null) {
                IOUtils.closeQuietly(outputStream);
            }
        }
        LOGGER.info("build Normal2OWExport zip successfully:" + zipFile.getAbsolutePath());
        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);
        //分页读取答案，每一次取出10000条,然后往文件中10000条数据
        int skip = 0;
        int limit = 10000;
        boolean over = false;
        File file = new File(dir, "ow_omr.txt");
        file.createNewFile();
        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 bufferedWriter = new BufferedWriter(outBatch);
        try {
            while (!over) {
                List<ExamStudentAnswer> examStudentAnswers = examStudentAnswerService.findObjectiveAnswerByExamCode(examPlan.getExamCode(), skip, limit);
                if (examStudentAnswers.size() < limit) {
                    over = true;
                }
                for (ExamStudentAnswer answer : examStudentAnswers) {
                    if (answer == null || StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                        continue;
                    }
//                    if (answer.isAbsent()) {
                    if (answer.getIsAbsent()) {
                        continue;
                    }
//                    ExamStudent examStudent = answer.getStudent();
                    ExamStudent examStudent = examStudentService.getById(answer.getStudentId());
                    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();
                skip = skip + limit;
            }
        } finally {
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
            if (fileOutputStream != null) {
                IOUtils.closeQuietly(fileOutputStream);
            }
        }
        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 void buildSubjectiveFile(ExamPlan examPlan, File dir,
                                       ZipOutputStream zipOutputStream) throws IOException {
        //20201029——加入score.txt逻辑
        File scoreFile = null;
        File subjectiveFile = null;
        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outBatch = null;
        BufferedWriter bufferedWriter = null;
        StringBuffer scoreResult = null;
        StringBuffer subjectiveResult = null;
        if (Objects.nonNull(examPlan.getSceneScore()) && Objects.equals(examPlan.getSceneScore(), SceneScore.YES)) {
            scoreFile = new File(dir, "score.txt");
            scoreFile.createNewFile();
            fileOutputStream = new FileOutputStream(scoreFile);
            outBatch = new OutputStreamWriter(fileOutputStream, SystemConstant.CHARSET_NAME);
            bufferedWriter = new BufferedWriter(outBatch);
            scoreResult = new StringBuffer();
        } else {
            subjectiveFile = new File(dir, "subjective.txt");
            subjectiveFile.createNewFile();
            fileOutputStream = new FileOutputStream(subjectiveFile);
            outBatch = new OutputStreamWriter(fileOutputStream, SystemConstant.CHARSET_NAME);
            bufferedWriter = new BufferedWriter(outBatch);
            subjectiveResult = new StringBuffer();
        }
        ExamStudentAnswerService examStudentAnswerService = SpringContextHolder.getBean(ExamStudentAnswerService.class);
        LinkedMultiValueMap<String, SubjectDto> subjectiveList = new LinkedMultiValueMap<>();
//        MongoOperations mongoTemplate = BeanFactory.getBean(MongoOperations.class);
//        DBObject query = new BasicDBObject();
//        query.put("exam_code", examPlan.getExamCode());
//        query.put("is_absent", false);
//        query.put("decode_file", new BasicDBObject("$exists", true));
//        DBObject porjection = new BasicDBObject("decode_file.access_uri", 1);
//        DBCursor dbCursor = mongoTemplate.getCollection("jk_exam_student_answer").find(query, porjection);
        QueryWrapper<ExamStudentAnswer> examStudentAnswerQueryWrapper = new QueryWrapper<>();
        examStudentAnswerQueryWrapper.lambda().eq(ExamStudentAnswer::getExamCode, examPlan.getExamCode())
                .eq(ExamStudentAnswer::getIsAbsent, false)
                .isNotNull(ExamStudentAnswer::getDecodeFileId);
        List<ExamStudentAnswer> examStudentAnswerList = examStudentAnswerService.list(examStudentAnswerQueryWrapper);
        Set<String> attachments = new LinkedHashSet<String>();
        AttachmentService attachmentService = SpringContextHolder.getBean(AttachmentService.class);
        for (ExamStudentAnswer examStudentAnswer : examStudentAnswerList) {
            Attachment attachment = attachmentService.getById(examStudentAnswer.getDecodeFileId());
            if (Objects.nonNull(attachment) && StringUtils.isNoneEmpty(attachment.getAccessUri())) {
                attachments.add(attachment.getAccessUri());
            }
        }
//        while (dbCursor.hasNext()) {
//            DBObject dbObject = dbCursor.next();
//            if (dbObject.get("decode_file") != null) {
//                DBObject decoeobj = (DBObject) dbObject.get("decode_file");
//                String accessUri = decoeobj.get("access_uri").toString();
//                if (StringUtils.isNoneEmpty(accessUri)) {
//                    attachments.add(accessUri);
//                }
//            }
//        }
        FileStoreUtil fileStoreConfig = SpringContextHolder.getBean(FileStoreUtil.class);
        String rootstr = fileStoreConfig.getStorageRoot();
        for (String accessUri : attachments) {
            InputStream inputStream = null;
            ZipInputStream zInputStream = null;
            ZipFile zipf = null;
            try {
                File root = new File(rootstr);
                //2020-12-19日加入阿里云下载
                File zipFile = null;
                if (fileStoreConfig.storageIsOss()) {
                    StringJoiner stringJoiner = new StringJoiner("").add(fileStoreConfig.getStorageRoot())
                            .add(File.separator).add(accessUri);
                    zipFile = fileStoreConfig.ossDownload(stringJoiner.toString().replaceAll("\\\\", "/"), fileStoreConfig.getStorageRoot() + File.separator + accessUri, fileStoreConfig.getStorageRoot());
                } else {
                    zipFile = new File(root, accessUri);
                }
                zipf = new ZipFile(zipFile);
                if (zipFile.exists()) {
                    inputStream = new FileInputStream(zipFile);
                    zInputStream = new ZipInputStream(inputStream, Charset.forName(SystemConstant.CHARSET_NAME));
                    ZipEntry zipEntry = null;
                    while ((zipEntry = zInputStream.getNextEntry()) != null) {
                        LOGGER.info("ow:" + zipEntry.getName());
                        if (!zipEntry.isDirectory() && zipEntry.getSize() > 0 && (
                                (zipEntry.getName().indexOf("/") != -1 || zipEntry.getName().indexOf("\\") != -1))) {
                            if (zipEntry.getName().indexOf("log") == -1) {
                                zipOutputStream.putNextEntry(zipEntry);
                                InputStream stream = zipf.getInputStream(zipEntry);
                                IOUtils.copy(stream, zipOutputStream);
                                zipOutputStream.closeEntry();
                                IOUtils.closeQuietly(stream);
                            }
                        } else if (Objects.nonNull(subjectiveFile) && !zipEntry.isDirectory() && zipEntry.getSize() > 0 && zipEntry.getName().contains("subjective")) {
                            LOGGER.info("写入subjective");
                            String[] strs = accessUri.split("/");
                            Long date = format.parse(strs[5]).getTime();
                            BufferedReader br = new BufferedReader(
                                    new InputStreamReader(zipf.getInputStream(zipEntry)));
                            String line;
                            while ((line = br.readLine()) != null) {
                                String string = line.substring(0, line.lastIndexOf(","));
                                SubjectDto subjectDto = new SubjectDto(accessUri, string, line, date);
                                subjectiveList.add(string, subjectDto);
                            }
                            br.close();
                        } else if (Objects.nonNull(scoreFile) && !zipEntry.isDirectory() && zipEntry.getSize() > 0 && zipEntry.getName().contains("score")) {
                            //20201029——加入score.txt逻辑
                            LOGGER.info("写入分数");
                            BufferedReader br = new BufferedReader(
                                    new InputStreamReader(zipf.getInputStream(zipEntry)));
                            String line;
                            while ((line = br.readLine()) != null) {
                                scoreResult.append(line).append("\r\n");
                            }
//                            scoreResult.append("\r\n");
                            br.close();
                        }
                    }
                }
            } catch (ParseException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    IOUtils.closeQuietly(inputStream);
                }
                if (zInputStream != null) {
                    IOUtils.closeQuietly(zInputStream);
                }
                if (zipf != null) {
                    IOUtils.closeQuietly(zipf);
                }
            }
        }
        if (Objects.nonNull(subjectiveList) && Objects.nonNull(subjectiveResult)) {
            Map<String, SubjectDto> map = new HashMap<>();
            subjectiveList.forEach((k, v) -> {
                for (int i = 0; i < v.size(); i++) {
                    SubjectDto subjectDto = (SubjectDto) v.get(i);
                    if (!map.containsKey(k)) {
                        map.put(k, subjectDto);
                    } else {
                        SubjectDto mapSubjectDto = map.get(k);
                        if (subjectDto.getDate() > mapSubjectDto.getDate()) {
                            map.put(k, subjectDto);
                        }
                    }
                }
            });
            StringBuffer finalSubjectiveResult = subjectiveResult;
            map.forEach((k, v) -> {
                SubjectDto subjectDto = (SubjectDto) v;
                finalSubjectiveResult.append(subjectDto.getLine()).append("\r\n");
            });
            bufferedWriter.write(subjectiveResult.toString());
            bufferedWriter.flush();
            InputStream inputStream = new FileInputStream(subjectiveFile);
            try {
                zipOutputStream.putNextEntry(new ZipEntry(subjectiveFile.getName()));
                IOUtils.copy(inputStream, zipOutputStream);
            } finally {
                if (inputStream != null) {
                    IOUtils.closeQuietly(inputStream);
                }
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (outBatch != null) {
                    outBatch.close();
                }
                zipOutputStream.closeEntry();
            }
        } else if (Objects.nonNull(scoreResult)) {
            bufferedWriter.write(scoreResult.toString());
            bufferedWriter.flush();
            InputStream inputStream = new FileInputStream(scoreFile);
            try {
                zipOutputStream.putNextEntry(new ZipEntry(scoreFile.getName()));
                IOUtils.copy(inputStream, zipOutputStream);
            } finally {
                if (inputStream != null) {
                    IOUtils.closeQuietly(inputStream);
                }
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
                if (outBatch != null) {
                    outBatch.close();
                }
                zipOutputStream.closeEntry();
            }
        }
    }

    //增加一个描述文件 准考证号-试卷ID
    protected void buildStudentPaperFile(ExamPlan examPlan, File dir, ZipOutputStream zipOutputStream) throws IOException {
        ExamStudentAnswerService examStudentAnswerService = SpringContextHolder.getBean(ExamStudentAnswerService.class);
        ExamStudentService examStudentService = SpringContextHolder.getBean(ExamStudentService.class);
        //分页读取答案，每一次取出10000条,然后往文件中10000条数据
        int skip = 0;
        int limit = 10000;
        boolean over = false;
        File file = new File(dir, "student-paper.txt");
        file.createNewFile();
        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 bufferedWriter = new BufferedWriter(outBatch);
        try {
            while (!over) {
                List<ExamStudentAnswer> examStudentAnswers = examStudentAnswerService.findObjectiveAnswerByExamCode(examPlan.getExamCode(), skip, limit);
                if (examStudentAnswers.size() < limit) {
                    over = true;
                }
                for (ExamStudentAnswer answer : examStudentAnswers) {
                    if (answer == null || StringUtils.isEmpty(answer.getObjectiveAnswer())) {
                        continue;
                    }
//                    if (answer.isAbsent()) {
                    if (answer.getIsAbsent()) {
                        continue;
                    }
//                    ExamStudent examStudent = answer.getStudent();
                    ExamStudent examStudent = examStudentService.getById(answer.getStudentId());
                    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());
                    stringBuffer.append("\r\n");
                    bufferedWriter.write(stringBuffer.toString());
                }
                bufferedWriter.flush();
                skip = skip + limit;
            }
        } finally {
            if (bufferedWriter != null) {
                bufferedWriter.close();
            }
            if (fileOutputStream != null) {
                IOUtils.closeQuietly(fileOutputStream);
            }
        }
        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();
        }
    }
}
