package com.qmth.jkserver.core.kj.validate;

import com.qmth.jkserver.constant.SpringContextHolder;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.core.kj.IKjValidateAndStore;
import com.qmth.jkserver.core.kj.plugin.NormalKjStore;
import com.qmth.jkserver.core.kj.plugin.NormalKjValidate;
import com.qmth.jkserver.enums.ProgressType;
import com.qmth.jkserver.model.Attachment;
import com.qmth.jkserver.model.ExamStudent;
import com.qmth.jkserver.service.AttachmentService;
import com.qmth.jkserver.util.FileStoreUtil;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 通用机考数据校验存储
 *
 * @author tianjun
 */
public class NormalKjValidateAndStore implements IKjValidateAndStore {

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

    private String examCode;

    // 加密数据文件ID
    private Long encryptedFileId;

    private String placeCode;

    private String roomCode;

    public NormalKjValidateAndStore(String examCode, Long encryptedFileId, String placeCode, String roomCode) {
        this.examCode = examCode;
        this.encryptedFileId = encryptedFileId;
        this.placeCode = placeCode;
        this.roomCode = roomCode;
    }

    public NormalKjValidateAndStore(String examCode, Long encryptedFileId) {
        this.examCode = examCode;
        this.encryptedFileId = encryptedFileId;
    }

    @Override
    public void execute(ProgressType type) throws Exception {
        Map<String, ExamStudent> STUDENT_CAHE = new ConcurrentHashMap<String, ExamStudent>();
        // 解密
        LOGGER.info("..decode start..");
        File zip = decodeZip(encryptedFileId);
        LOGGER.info("..decode end..");
        // 1、数据校验
        LOGGER.info("..normalKjValidate start..");
        NormalKjValidate normalKjValidate = new NormalKjValidate(zip, examCode, STUDENT_CAHE);
        normalKjValidate.execute();
        LOGGER.info("..normalKjValidate end..");
        // 2、数据存储
        LOGGER.info("..NormalKjStore start..");
        NormalKjStore normalKjDataStore = new NormalKjStore(zip, examCode, encryptedFileId, placeCode,
                roomCode, STUDENT_CAHE);
        normalKjDataStore.execute(type);
        LOGGER.info("..NormalKjStore end..");
    }

    // zip数据包的解压
    protected File decodeZip(Long encryptedFileId) throws Exception {
        //2020-12-19日加入阿里云下载
        AttachmentService attachmentService = SpringContextHolder.getBean(AttachmentService.class);
        FileStoreUtil fileStoreConfig = SpringContextHolder.getBean(FileStoreUtil.class);
        Attachment encrypted = attachmentService.findByIdService(encryptedFileId);
        File encryptedFile = null;
        if (fileStoreConfig.workIsOss()) {
            StringJoiner stringJoiner = new StringJoiner("").add(fileStoreConfig.getWorkRoot())
                    .add(File.separator).add(encrypted.getAccessUri());
            encryptedFile = fileStoreConfig.ossDownload(stringJoiner.toString().replaceAll("\\\\", "/"), fileStoreConfig.getWorkRoot() + File.separator + encrypted.getAccessUri(), fileStoreConfig.getWorkRoot());
        } else {
            encryptedFile = new File(fileStoreConfig.getWorkRoot(), encrypted.getAccessUri());
        }
        if (!encryptedFile.exists()) {
            throw new JkServerException("监考端上传的原始考试数据包不存在");
        }
        File decodedZip = decode(encryptedFile);
        return decodedZip;
    }

    @SuppressWarnings("resource")
    public File decode(File file) throws IOException {
        byte[] startBytes = new byte[23]; // zip头部块
        byte[] endBytes = new byte[23]; // zip尾部块
        byte[] loop = new byte[41];
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");// 加密的文件格式为qm
        File ansFile = new File(file.getParentFile(), file.getName() + SystemConstant.ZIP_PREFIX);
        if (!ansFile.exists()) {
            ansFile.createNewFile();
        }
        BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(ansFile));
        if (file.length() < (136 + 54)) {
            throw new JkServerException("文件结构错误");
        }
        randomAccessFile.seek(file.length() - 54);
        int len = 0;
        len = randomAccessFile.read(startBytes);
        os.write(startBytes);
        if (len != 23) {
            throw new JkServerException("文件结构错误,头部23字节读取失败");
        }
        len = randomAccessFile.read(endBytes);
        if (len != 23) {
            throw new JkServerException("文件结构错误,尾部23字节读取失败");
        }
        long total = file.length() - 54 - 136;
        // 每次读取41字节 计算需要读取多少次，以及最后一次需要读取多少字节
        int count = new Double(Math.floor(total / 41)).intValue(); // 读取次数
        int left = new Double(total % 41).intValue(); // 最后一次读取多少字节
        // 开始读取中间的部分
        randomAccessFile.seek(file.length() - 54 - left);
        byte[] leftBytes = new byte[left];
        len = randomAccessFile.read(leftBytes);
        if (len != left) {
            throw new JkServerException("加密文件结构错误,尾部字节读取错误");
        } else {
            os.write(leftBytes);
        }
        for (int i = 0; i < count; i++) {
            randomAccessFile.seek(file.length() - 54 - left - (i + 1) * 41);
            len = randomAccessFile.read(loop);
            if (len != 41) {
                throw new JkServerException("加密文件结构错误");
            } else {
                os.write(loop);
            }
        }
        os.write(endBytes);
        IOUtils.closeQuietly(os);
        randomAccessFile.close();
        if (ansFile.length() != file.length() - 144) {
            throw new JkServerException("文件解码失败");
        }
        return ansFile;
    }

}
