package com.qmth.jkserver.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.jkserver.common.UnZipUtils;
import com.qmth.jkserver.constant.SpringContextHolder;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.dao.ExamPaperTempDao;
import com.qmth.jkserver.dto.FileDto;
import com.qmth.jkserver.model.*;
import com.qmth.jkserver.params.ExamPaperUncoilingParams;
import com.qmth.jkserver.service.AttachmentService;
import com.qmth.jkserver.service.ExamPaperTempService;
import com.qmth.jkserver.service.ExamStudentService;
import com.qmth.jkserver.service.KWService;
import com.qmth.jkserver.util.FileStoreUtil;
import net.lingala.zip4j.core.ZipFile;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description: 试卷临时service impl
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2021/5/12
 */
@Service
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
public class ExamPaperTempServiceImpl extends ServiceImpl<ExamPaperTempDao, ExamPaperTemp> implements ExamPaperTempService {
    private static final Logger log = LoggerFactory.getLogger(ExamPaperTempServiceImpl.class);

    @Resource
    ExamPaperTempDao paperTempDao;

    @Resource
    @Lazy
    KWService kwService;

    @Resource
    AttachmentService attachmentService;

    @Resource
    @Lazy
    ExamStudentService examStudentService;

//    @Resource
//    MongoTemplate mongoTemplate;

    /**
     * 查询所有考试科目
     *
     * @param examCode
     * @return
     */
    @Override
    public List<ExamPaperTemp> findExamCourseAllByTask(String examCode) {
//        List<ExamPaperTemp> examPaperTempList = paperTempDao.findByExamCode(examCode);
//        return examPaperTempList.stream().filter(SystemConstant.distinctByKey(s -> s.getCourseCode())).collect(Collectors.toList());
        return paperTempDao.findByExamCode(examCode);
    }

    /**
     * 获取试卷答案json文件
     *
     * @param examCode
     * @return
     * @throws Exception
     */
    @Override
    public FileDto getPaperAnswerJsonFile(String examCode) throws Exception {
        FileStoreUtil fileStoreConfig = SpringContextHolder.getBean(FileStoreUtil.class);
        List<File> paperAnswerFile = null;
        KwFile kwFile = kwService.findKwFileByExamCode(examCode);
        if (Objects.isNull(kwFile)) {
            throw new JkServerException(JkServerException.SYSTEM_EXCEPTION.getCode(), "该考试任务下考务文件数据错误");
        }
        if (Objects.isNull(kwFile.getPaperAnswerFileId())) {
            throw new JkServerException("试卷标准答案未上传");
        }
        List<String> deletePaths = new ArrayList<>();
        //获取试卷
        Attachment attachmentPaperFile = attachmentService.getById(kwFile.getPaperFileId());
//        File paperTarget = SystemConstant.getAttachmentFile(kwFile.getPaperFile());
        File paperTarget = SystemConstant.getAttachmentFile(attachmentPaperFile);
        if (fileStoreConfig.workIsOss()) {
            deletePaths.add(paperTarget.getAbsolutePath());
        }

        //获取试卷标准答案
        Attachment attachmentPaperAnswerFile = attachmentService.getById(kwFile.getPaperAnswerFileId());
//        File paperAnswerTarget = SystemConstant.getAttachmentFile(kwFile.getPaperAnswerFile());
        File paperAnswerTarget = SystemConstant.getAttachmentFile(attachmentPaperAnswerFile);
        if (fileStoreConfig.workIsOss()) {
            deletePaths.add(paperAnswerTarget.getAbsolutePath());
        }

        File answerDir = new File(paperAnswerTarget.getParentFile() + File.separator + SystemConstant.getNanoId());
        UnZipUtils.doUnZip(paperAnswerTarget, answerDir, paperAnswerTarget.getParent());
        deletePaths.add(answerDir.getAbsolutePath());

//        Map<String, Object> map = new LinkedHashMap<>();
//        map = SystemConstant.getFileDirectoryCount(answerDir, map);

//        File finalAnswerDir = answerDir;
//        if (!CollectionUtils.isEmpty(map)) {
//            for (Map.Entry<String, Object> entry : map.entrySet()) {
//                if (!Objects.equals(entry.getKey(), "fileDirectoryCount")) {
//                    finalAnswerDir = new File((String) entry.getValue());
//                    if (finalAnswerDir.getAbsolutePath().endsWith("answer")) {
//                        break;
//                    }
//                }
//            }
//        } else {
//            finalAnswerDir = answerDir;
//        }

//        File finalAnswerDir = null;
//        if (!CollectionUtils.isEmpty(map)) {
//            log.info("fileDirectoryCount:{}", JacksonUtil.parseJson(map));
//            int fileDirectoryCount = (int) map.get("fileDirectoryCount");
//            if (fileDirectoryCount == 3) {
//                finalAnswerDir = new File((String) map.get("1"));
//            } else if (fileDirectoryCount == 4) {
//                finalAnswerDir = new File((String) map.get("2"));
//            }
//        } else {
//            finalAnswerDir = answerDir;
//        }

        //获取开卷密码
        List<ExamPaperUncoiling> examPaperUncoilingList = kwService.queryExamPaperUncoiling(new ExamPaperUncoilingParams(examCode));
        Map<String, File> mapFile = new HashMap<>();
        mapFile = analyzePaperZip(paperTarget, examPaperUncoilingList, mapFile, deletePaths);

        List<ExamPaperTemp> examPaperTempList = this.findExamCourseAllByTask(examCode);
        if (Objects.nonNull(examPaperTempList) && examPaperTempList.size() > 0) {
            Map<String, ExamPaperTemp> paperCourseCodeTempMap = examPaperTempList.stream().collect(
                    Collectors.toMap(s -> s.getCourseCode() + SystemConstant.JOINT_MARK + s.getPaperId() + SystemConstant.JSON_PREFIX, Function.identity(), (dto1, dto2) -> dto1));
            Map<String, File> finalMapFile = mapFile;
            paperAnswerFile = Objects.isNull(paperAnswerFile) ? new ArrayList<>() : paperAnswerFile;
            List<File> finalPaperAnswerFile = paperAnswerFile;
            File finalAnswerDir1 = answerDir;
            paperCourseCodeTempMap.forEach((k, v) -> {
                finalPaperAnswerFile.add(createPaperAnswerFile(k, finalMapFile, finalAnswerDir1));
            });
        }
        FileDto fileDto = new FileDto(paperAnswerFile);
        fileDto.setDeletePaths(deletePaths);
        return fileDto;
    }

    /**
     * 查找题目
     *
     * @param questions
     * @param answers
     * @param acceptObjective
     * @param acceptSubjective
     * @return
     */
    protected JSONArray filterQuestions(JSONArray questions, JSONArray answers, boolean acceptObjective,
                                        boolean acceptSubjective) {
        JSONArray collection = new JSONArray();
        for (int i = 0; i < questions.size(); i++) {
            JSONObject question = questions.getJSONObject(i);
            //根据题号查找答案
            JSONObject answer = findJsonObject(answers, question.getIntValue("number"));
            boolean accept = true;
            //判断结构类型
            switch (question.getIntValue("structType")) {
                case 1:
                    //单选
                case 2:
                    //多选
                case 3:
                    //判断
                    accept = acceptObjective;
                    break;
                case 4:
                    //填空
                case 5:
                    //问答
                    accept = acceptSubjective;
                    break;
                case 6:
                    //套题
                case 7:
                    //听力
                case 8:
                    //配对题
                    //按条件过滤套题下所有子题
                    JSONArray subAnswers = answer != null ? answer.getJSONArray("subQuestions") : null;
                    JSONArray subCollection = filterQuestions(question.getJSONArray("subQuestions"), subAnswers,
                            acceptObjective, acceptSubjective);
                    //没有子题，则本题也被过滤掉
                    if (subCollection.size() > 0) {
                        question.put("subQuestions", subCollection);
                    } else {
                        accept = false;
                    }
                    break;
                default:
                    accept = false;
            }
            if (accept) {
                if (Objects.nonNull(answer)) {
                    question.put("answer", answer.get("answer"));
                }
                collection.add(question);
            }
        }
        return collection;
    }

    /**
     * 查找题型
     *
     * @param array
     * @param number
     * @return
     */
    protected JSONObject findJsonObject(JSONArray array, int number) {
        if (Objects.nonNull(array)) {
            for (int i = 0; i < array.size(); i++) {
                JSONObject item = array.getJSONObject(i);
                if (item.getIntValue("number") == number) {
                    return item;
                }
            }
        }
        return null;
    }

    /**
     * 解析zip文件
     *
     * @param target
     * @param examPaperUncoilingList
     * @param mapFile
     * @param deletePaths
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, File> analyzePaperZip(File target, List<ExamPaperUncoiling> examPaperUncoilingList, Map<String, File> mapFile, List<String> deletePaths) throws Exception {
        String format = target.getName().substring(target.getName().lastIndexOf("."));
        if (format.equalsIgnoreCase(SystemConstant.ZIP_PREFIX)) {//解析试卷
            ZipFile zipFile = new ZipFile(target.getAbsolutePath());
            if (!zipFile.isEncrypted()) {
                File paperDir = new File(target.getParentFile() + File.separator + SystemConstant.getNanoId());
                deletePaths.add(paperDir.getAbsolutePath());
                List<File> paperFiles = UnZipUtils.doUnZip(target, paperDir, target.getParent());
                for (File paperfile : paperFiles) {
                    mapFile.computeIfAbsent(paperfile.getName(), v -> paperfile);
                    analyzePaperZip(paperfile, examPaperUncoilingList, mapFile, deletePaths);
                }
            } else {
                for (ExamPaperUncoiling examPaperUncoiling : examPaperUncoilingList) {
                    Optional.ofNullable(examPaperUncoiling.getPassword()).orElseThrow(() -> new JkServerException("考试编号[" + examPaperUncoiling.getExamCode() + "]批次:" + examPaperUncoiling.getBatchId() + "未设置开卷密码!"));
                    String fileName = target.getName();
                    String[] strs = fileName.substring(0, fileName.indexOf(".")).split("_");
                    if (strs.length > 1 && Objects.nonNull(strs[1]) && Objects.equals(strs[1].trim(), examPaperUncoiling.getBatchId().trim())) {
                        List<File> fileList = kwService.unZipByPasswordByDest(target, examPaperUncoiling.getPassword());
                        if (Objects.nonNull(fileList) && fileList.size() > 0) {
                            for (File file : fileList) {
                                mapFile.computeIfAbsent(file.getName(), v -> file);
                                analyzePaperZip(file, examPaperUncoilingList, mapFile, deletePaths);
                            }
                        }
                    }
                }
            }
        }
        return mapFile;
    }

    /**
     * 获取考生作答json文件
     *
     * @param examCode
     * @param examStudentAnswerList
     * @param attachmentMap
     * @return
     * @throws IOException
     */
    @Override
    public FileDto getExamStudentAnswerJsonFile(String examCode, List<ExamStudentAnswer> examStudentAnswerList, Map<Long, Attachment> attachmentMap) throws IOException {
        FileStoreUtil fileStoreConfig = SpringContextHolder.getBean(FileStoreUtil.class);
//        Query jkExamStudentAnswerQuery = new Query();
//        jkExamStudentAnswerQuery.addCriteria(Criteria.where("examCode").is(examCode));
//        jkExamStudentAnswerQuery.addCriteria(Criteria.where("isAbsent").is(false));
//        jkExamStudentAnswerQuery.addCriteria(Criteria.where("decodeFile").exists(true));
//        List<ExamStudentAnswer> examStudentAnswerList = this.mongoTemplate.find(jkExamStudentAnswerQuery, ExamStudentAnswer.class);
//        if (Objects.isNull(examStudentAnswerList) || examStudentAnswerList.size() == 0) {
//            throw new JkServerException("考生考试记录为空");
//        }
        //Map<String, ExamStudentAnswer> examStudentAnswerMap = examStudentAnswerList.stream().collect(Collectors.toMap(e -> e.getDecodeFile().getAccessUri(), Function.identity(), (dto1, dto2) -> dto1));
        Map<String, List<ExamStudentAnswer>> examStudentAnswerMap = new LinkedHashMap<>();
        for (ExamStudentAnswer examStudentAnswer : examStudentAnswerList) {
            Attachment attachment = attachmentService.getById(examStudentAnswer.getDecodeFileId());
            if (!examStudentAnswerMap.containsKey(attachment.getAccessUri())) {
                examStudentAnswerMap.put(attachment.getAccessUri(), new ArrayList<>(Arrays.asList(examStudentAnswer)));
            } else {
                List<ExamStudentAnswer> examStudentAnswers = examStudentAnswerMap.get(attachment.getAccessUri());
                examStudentAnswers.add(examStudentAnswer);
                examStudentAnswerMap.put(attachment.getAccessUri(), examStudentAnswers);
            }
        }
        List<String> deletePaths = new ArrayList<>();
        List<LinkedMultiValueMap<String, File[]>> linkedMultiValueMapList = new ArrayList<>();
        examStudentAnswerMap.forEach((k, v) -> {
            LinkedMultiValueMap<String, File[]> linkedMultiValueMap = new LinkedMultiValueMap<>();
            for (ExamStudentAnswer es : v) {
                Attachment attachment = null;
                if (!attachmentMap.containsKey(es.getDecodeFileId())) {
                    attachment = attachmentService.getById(es.getDecodeFileId());
                    File target = null;
                    try {
                        target = SystemConstant.getAttachmentFile(attachment);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (fileStoreConfig.workIsOss()) {
                        deletePaths.add(target.getParentFile().getAbsolutePath());
                    }
                    File explodeTempDir = new File(target.getParentFile() + File.separator + SystemConstant.getNanoId());
                    try {
                        UnZipUtils.doUnZip(target, explodeTempDir, target.getParent());
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    File fileJson = new File(explodeTempDir.getAbsolutePath() + File.separator + "json");
                    if (!fileJson.exists()) {
                        throw new JkServerException(JkServerException.SYSTEM_EXCEPTION.getCode(), "考生作答文件在服务器上不存在");
                    }
                    attachment.setFileJson(fileJson);
                    attachmentMap.put(es.getDecodeFileId(), attachment);
                } else {
                    attachment = attachmentMap.get(es.getDecodeFileId());
                }
                ExamStudent examStudent = examStudentService.getById(es.getStudentId());
                if (!linkedMultiValueMap.containsKey(examStudent.getBatchId())) {
                    linkedMultiValueMap.add(examStudent.getBatchId(), attachment.getFileJson().listFiles());
                }
            }
            linkedMultiValueMapList.add(linkedMultiValueMap);
        });
        return new FileDto(linkedMultiValueMapList, deletePaths);
    }

    /**
     * 创建试卷答案文件
     *
     * @param k
     * @param mapFile
     * @param answerDir
     * @return
     */
    public File createPaperAnswerFile(String k, Map<String, File> mapFile, File answerDir) {
        File file = mapFile.get(k);
//        log.info("file:{}", JacksonUtil.parseJson(file));
        InputStream in = null;
        ByteArrayOutputStream ou = null;
        OutputStream out = null;
        File answerFileDir = null;
        try {
            in = new FileInputStream(file);
            ou = new ByteArrayOutputStream();
            IOUtils.copy(in, ou);
            JSONObject paperJson = JSONObject.parseObject(new String(ou.toByteArray(), StandardCharsets.UTF_8));
//            log.info("paperJson:{}", paperJson.toJSONString());
            in.close();
            ou.flush();
            ou.close();

            String answerTemp = k.substring(0, k.lastIndexOf("."));
            String[] answerFileNames = answerTemp.split(SystemConstant.JOINT_MARK);
            File answerDirFile = new File(answerDir.getAbsolutePath() + File.separator + answerFileNames[0] + File.separator + answerFileNames[1]);
            if (!answerDirFile.exists()) {
                return answerFileDir;
            }
            File[] answerFiles = answerDirFile.listFiles();
            if (Objects.isNull(answerFiles) || answerFiles.length == 0) {
                return answerFileDir;
            }
            in = new FileInputStream(answerFiles[0]);
            ou = new ByteArrayOutputStream();
            IOUtils.copy(in, ou);
            log.info("ext:{},path{}", FilenameUtils.getExtension(file.getPath()), file.getAbsolutePath());
            JSONObject answerJson = JSONObject.parseObject(new String(ou.toByteArray(), StandardCharsets.UTF_8));
//            log.info("answerJson:{}", answerJson.toJSONString());
            JSONArray answerDetails = answerJson.getJSONArray("details");

            JSONArray detailCollection = new JSONArray();
            JSONArray paperDetails = paperJson.getJSONArray("details");
            if (Objects.nonNull(paperDetails)) {
                for (int i = 0; i < paperDetails.size(); i++) {
                    //遍历所有大题
                    JSONObject paperDetail = paperDetails.getJSONObject(i);
                    JSONObject answerDetail = findJsonObject(answerDetails, paperDetail.getIntValue("number"));
                    //按条件过滤需要的小题同时合并答案
                    JSONArray questionCollection = filterQuestions(paperDetail.getJSONArray("questions"),
                            answerDetail != null ? answerDetail.getJSONArray("questions") : null, true,
                            true);
                    //有小题的情况下，本大题才需要被包含
                    if (questionCollection.size() > 0) {
                        paperDetail.put("questions", questionCollection);
                        detailCollection.add(paperDetail);
                    }
                }
                paperJson.put("details", detailCollection);
            }
            answerFileDir = new File(file.getParent() + File.separator + "paperAnswer" + File.separator + k);
            if (!answerFileDir.exists()) {
                answerFileDir.getParentFile().mkdirs();
                answerFileDir.createNewFile();
            }
            out = new FileOutputStream(answerFileDir);
            out.write(paperJson.toString().getBytes(StandardCharsets.UTF_8));
//            log.info("paperJson update:{}", paperJson.toJSONString());
        } catch (FileNotFoundException e) {
            log.error(SystemConstant.LOG_ERROR, e);
            e.printStackTrace();
        } catch (IOException e) {
            log.error(SystemConstant.LOG_ERROR, e);
            e.printStackTrace();
        } finally {
            try {
                if (Objects.nonNull(in)) {
                    in.close();
                }
                if (Objects.nonNull(ou)) {
                    ou.flush();
                    ou.close();
                }
                if (Objects.nonNull(out)) {
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return answerFileDir;
    }
}
