package com.qmth.jkserver.controller.api;

import com.qmth.jkserver.common.ZipUtils;
import com.qmth.jkserver.constant.SystemConstant;
import com.qmth.jkserver.core.exception.ApiException;
import com.qmth.jkserver.core.exception.JkServerException;
import com.qmth.jkserver.enums.ExamKwProgressType;
import com.qmth.jkserver.enums.ExamStatus;
import com.qmth.jkserver.model.*;
import com.qmth.jkserver.service.AttachmentService;
import com.qmth.jkserver.service.ExamStudentService;
import com.qmth.jkserver.service.JkExamPlanService;
import com.qmth.jkserver.service.KWService;
import com.qmth.jkserver.util.FileStoreUtil;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;

/**
 * 考务数据抓取相关接口
 *
 * @author tianjun
 */
@RestController
@RequestMapping("/api/jkdevice")
public class ExamKwController {

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

    @Resource
    @Lazy
    private JkExamPlanService examPlanService;

    @Resource
    @Lazy
    private ExamStudentService examStudentService;

    @Resource
    private FileStoreUtil fileStoreConfig;

    @Resource
    @Lazy
    private KWService kWService;

    @Resource
    AttachmentService attachmentService;

    @RequestMapping("/getPlaceRooms")
    public void getPlaceRooms(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        KwFile kwFile = kWService.findKwFileByExamCode(examCode);
        if (kwFile == null || kwFile.getRoomFileId() == null) {
            throw ApiException.KW_STORE_DATA_ERROR;
        }
//        Attachment attachment = kwFile.getRoomFile();
        Attachment attachment = attachmentService.getById(kwFile.getRoomFileId());
        //2020-12-19日加入阿里云下载
        File target = null;
        if (fileStoreConfig.storageIsOss()) {
            StringJoiner stringJoiner = new StringJoiner("").add(fileStoreConfig.getStorageRoot())
                    .add(File.separator).add(attachment.getAccessUri());
            target = fileStoreConfig.ossDownload(stringJoiner.toString().replaceAll("\\\\", "/"), fileStoreConfig.getStorageRoot() + File.separator + attachment.getAccessUri(), fileStoreConfig.getStorageRoot());
        } else {
            target = new File(fileStoreConfig.getStorageRoot(), attachment.getAccessUri());
        }
        if (!target.exists()) {
            throw ApiException.KW_STORE_KC_ERROR;
        }
        response.addHeader("md5", DigestUtils.md5Hex(new FileInputStream(target)));
        String fileName = URLEncoder.encode(attachment.getOriginalName(), SystemConstant.CHARSET_NAME);
        response.addHeader("fileName", fileName);
        response.addHeader("fileSize", target.length() + "");
        response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
        response.setContentType("application/octet-stream");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(target);
            outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
            if (outputStream != null) {
                IOUtils.closeQuietly(outputStream);
            }
        }
    }

    // 考务数据下载接口
    @RequestMapping("/getKwData")
    public void getKwData(HttpServletRequest request, HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        String placeCode = request.getParameter("placeCode");
        if (StringUtils.isEmpty(placeCode)) {
            throw ApiException.PLACE_CODE_INVALID;
        }
        String roomCode = request.getParameter("roomCode");
        if (StringUtils.isEmpty(roomCode)) {
            throw ApiException.ROOM_CODE_INVALID;
        }
        KwFile kwFile = kWService.findKwFileByExamCode(examCode);
//        Attachment studentAttr = kwFile.getStudentFile();
        Attachment studentAttr = attachmentService.getById(kwFile.getStudentFileId());
        File storePath = new File(fileStoreConfig.getWorkRoot());
        storePath = new File(storePath, examCode);
        storePath = new File(storePath, "get_kw_data_tempdir");
        storePath = new File(storePath, placeCode + "_" + roomCode);
        storePath = new File(storePath, studentAttr.getMd5());
        if (!storePath.exists()) {
            storePath.mkdirs();
        }
        // 生成子文件
        File zipFile = new File(storePath, placeCode + "_" + roomCode + SystemConstant.ZIP_PREFIX);
        if (zipFile.exists()) {
            response.addHeader("md5", DigestUtils.md5Hex(new FileInputStream(zipFile)));
            String fileName = URLEncoder.encode("考生数据.zip", SystemConstant.CHARSET_NAME);
            response.addHeader("fileName", fileName);
            response.addHeader("fileSize", zipFile.length() + "");
            response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
            response.setContentType("application/octet-stream");
            InputStream inputStream = null;
            OutputStream outputStream = null;
            try {
                inputStream = new FileInputStream(zipFile);
                outputStream = response.getOutputStream();
                IOUtils.copy(inputStream, outputStream);
            } finally {
                if (inputStream != null) {
                    IOUtils.closeQuietly(inputStream);
                }
                if (outputStream != null) {
                    IOUtils.closeQuietly(outputStream);
                }
            }
            return;
        }
        zipFile.createNewFile();
        //2020-12-19日加入阿里云下载
        File studentFile = new File(storePath, "student.csv");
        if (!studentFile.exists()) {
            // 生成考生文件
            long time1 = System.currentTimeMillis();
            List<ExamStudent> examStudents = examStudentService.findJkDeviceKwStudents(examCode, placeCode, roomCode);
            long time2 = System.currentTimeMillis();
            LOGGER.info("[findJkDeviceKwStudents] use :" + (time2 - time1) / 1000 + "秒");
            BufferedWriter bufferedWriter = null;
            FileOutputStream fos1 = null;
            try {
                fos1 = new FileOutputStream(studentFile);
                fos1.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
                OutputStreamWriter out = new OutputStreamWriter(fos1, SystemConstant.CHARSET_NAME);
                bufferedWriter = new BufferedWriter(out);
                for (ExamStudent examStudent : examStudents) {
                    StringBuffer stringBuffer = new StringBuffer();
                    stringBuffer.append(invokeDefaultValue(examStudent.getBatchId())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getStageId())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getPlaceCode())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getRoomCode())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getTicketNo())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getIdcard())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getName())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getCourseCode())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStudent.getCourseName()));
                    stringBuffer.append("\r\n");// 换行符号
                    bufferedWriter.write(invokeDefaultValue(stringBuffer.toString()));
                }
                bufferedWriter.flush();
            } catch (Exception e) {
                e.printStackTrace();
                LOGGER.error("生成student.csv文件错误:", e);
                throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "生成student.csv文件错误");
            } finally {
                if (bufferedWriter != null) {
                    IOUtils.closeQuietly(bufferedWriter);
                }
                if (fos1 != null) {
                    IOUtils.closeQuietly(fos1);
                }
            }
        }
        //2020-12-19日加入阿里云下载
        File batchFile = new File(storePath, "batch.csv");
        if (!batchFile.exists()) {
            // 生成CSV文件
            long time3 = System.currentTimeMillis();
            List<ExamStage> examStages = examStudentService.findJkDeviceBatchStages(examCode);
            long time4 = System.currentTimeMillis();
            LOGGER.info("[findJkDeviceBatchStages] use :" + (time4 - time3) / 1000 + "s");
            BufferedWriter csvWriter = null;
            FileOutputStream fos2 = null;
            try {
                fos2 = new FileOutputStream(batchFile);
                fos2.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF});
                OutputStreamWriter outBatch = new OutputStreamWriter(fos2, SystemConstant.CHARSET_NAME);
                csvWriter = new BufferedWriter(outBatch);
                for (ExamStage examStage : examStages) {
                    StringBuffer stringBuffer = new StringBuffer();
                    stringBuffer.append(invokeDefaultValue(examStage.getBatchId())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStage.getBatchName())).append(",");
                    stringBuffer.append(invokeDefaultValue(formatDate(new Date(examStage.getBatchStartTime())))).append(",");
                    stringBuffer.append(invokeDefaultValue(formatDate(new Date(examStage.getBatchEndTime())))).append(",");
                    stringBuffer.append(invokeDefaultValue(examStage.getStageId())).append(",");
                    stringBuffer.append(invokeDefaultValue(formatDate(new Date(examStage.getStageStartTime())))).append(",");
                    stringBuffer.append(invokeDefaultValue(formatDate(new Date(examStage.getStageEndTime())))).append(",");
                    stringBuffer.append(invokeDefaultValue(examStage.getLateLoginTime())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStage.getFrozenTime())).append(",");
                    stringBuffer.append(invokeDefaultValue(examStage.getSdsk()));
                    stringBuffer.append("\r\n");// 换行符号
                    csvWriter.write(invokeDefaultValue(stringBuffer.toString()));
                }
                csvWriter.flush();
            } catch (Exception e) {
                LOGGER.error("生成batch.csv文件错误:", e);
                e.printStackTrace();
                throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "生成batch.csv文件错误");
            } finally {
                if (csvWriter != null) {
                    IOUtils.closeQuietly(csvWriter);
                }
                if (fos2 != null) {
                    IOUtils.closeQuietly(fos2);
                }
            }
        }
        ZipUtils.doZip(zipFile, new File[]{studentFile, batchFile});
        response.addHeader("md5", DigestUtils.md5Hex(new FileInputStream(zipFile)));
        String fileName = URLEncoder.encode("考生数据" + SystemConstant.ZIP_PREFIX, SystemConstant.CHARSET_NAME);
        response.addHeader("fileName", fileName);
        response.addHeader("fileSize", zipFile.length() + "");
        response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
        response.setContentType("application/octet-stream");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(zipFile);
            outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
            if (outputStream != null) {
                IOUtils.closeQuietly(outputStream);
            }
        }
    }

    // 获取开卷密码
    @RequestMapping("/getPaperPwd")
    public Object getPaperPwd(HttpServletRequest request, HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        String batchId = request.getParameter("batchId");
        if (StringUtils.isEmpty(batchId)) {
            throw ApiException.BATCH_ID_INVALID;
        }
        ExamPaperUncoiling examPaperUncoiling = kWService.getPaperPassword(examCode, batchId);
        if (examPaperUncoiling == null) {
            throw ApiException.PASSWORD_NOT_SET;
        }
        String password = examPaperUncoiling.getPassword();
        byte[] encodeBase64 = Base64.encodeBase64(password.getBytes(SystemConstant.CHARSET_NAME));
        password = new String(encodeBase64, SystemConstant.CHARSET_NAME);
        return password;
    }

    // 试卷数据下载接口
    @RequestMapping("/getPaper")
    public void getPaper(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        KwFile kwFile = kWService.findKwFileByExamCode(examCode);
        if (kwFile == null) {
            throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "该考试任务下考务文件数据错误");
        }
//        Attachment attachment = kwFile.getPaperFile();
        Attachment attachment = attachmentService.getById(kwFile.getPaperFileId());
        //2020-12-19日加入阿里云下载
        File storePath = null;
        File target = null;
        if (fileStoreConfig.storageIsOss()) {
            storePath = new File(fileStoreConfig.getStorageRoot());
            String path = storePath + File.separator + attachment.getAccessUri();
            target = fileStoreConfig.ossDownload(path, path, fileStoreConfig.getStorageRoot());
        } else {
            storePath = new File(fileStoreConfig.getStorageRoot());
            target = new File(storePath, attachment.getAccessUri());
        }
        if (!target.exists() || !target.isFile()) {
            throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "试卷文件在服务器上不存在");
        }
        response.addHeader("md5", DigestUtils.md5Hex(new FileInputStream(target)));
        String fileName = URLEncoder.encode(attachment.getOriginalName(), SystemConstant.CHARSET_NAME);
        response.addHeader("fileName", fileName);
        response.addHeader("fileSize", target.length() + "");
        response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
        response.setContentType("application/octet-stream");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(target);
            outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
            if (outputStream != null) {
                IOUtils.closeQuietly(outputStream);
            }
        }
    }

    //标准答案下载接口
    @RequestMapping("/getStandAnswer")
    public void getStandAnswer(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        KwFile kwFile = kWService.findKwFileByExamCode(examCode);
        if (kwFile == null) {
            throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "该考试任务下考务文件数据错误");
        }
//        Attachment attachment = kwFile.getSceneScoreFile();
        Attachment attachment = attachmentService.getById(kwFile.getSceneScoreFileId());
        //2020-12-19日加入阿里云下载
        File storePath = null;
        File target = null;
        if (fileStoreConfig.storageIsOss()) {
            storePath = new File(fileStoreConfig.getStorageRoot());
            String path = storePath + File.separator + attachment.getAccessUri();
            target = fileStoreConfig.ossDownload(path, path, fileStoreConfig.getStorageRoot());
        } else {
            storePath = new File(fileStoreConfig.getStorageRoot());
            target = new File(storePath, attachment.getAccessUri());
        }
        if (!target.exists() || !target.isFile()) {
            throw new ApiException(ApiException.SYSTEM_EXCEPTION.getCode(), "标答以及评分规则文件在服务器上不存在");
        }
        response.addHeader("md5", DigestUtils.md5Hex(new FileInputStream(target)));
        String fileName = URLEncoder.encode(attachment.getOriginalName(), SystemConstant.CHARSET_NAME);
        response.addHeader("fileName", fileName);
        response.addHeader("fileSize", target.length() + "");
        response.setCharacterEncoding(SystemConstant.CHARSET_NAME);
        response.setContentType("application/octet-stream");
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = new FileInputStream(target);
            outputStream = response.getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        } finally {
            if (inputStream != null) {
                IOUtils.closeQuietly(inputStream);
            }
            if (outputStream != null) {
                IOUtils.closeQuietly(outputStream);
            }
        }
    }

    // 考务数据以及试卷下载回调接口
    @RequestMapping("/notice")
    public Object notice(HttpServletRequest request, HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        String placeCode = request.getParameter("placeCode");
        if (StringUtils.isEmpty(placeCode)) {
            throw ApiException.PLACE_CODE_INVALID;
        }
        String roomCode = request.getParameter("roomCode");
        if (StringUtils.isEmpty(roomCode)) {
            throw ApiException.ROOM_CODE_INVALID;
        }
        Integer type = Integer.valueOf(request.getParameter("type"));
        if (type == null || ExamKwProgressType.findById(type) == null) {
            throw ApiException.NOTICE_TYPE_INVALID;
        }
        ExamKwProgress examKwProgress = new ExamKwProgress();
        examKwProgress.setId(SystemConstant.getDbUuid());
        examKwProgress.setExamCode(examCode);
        examKwProgress.setPlaceCode(placeCode);
        examKwProgress.setRoomCode(roomCode);
        examKwProgress.setDownload(true);
        examKwProgress.setTime(System.currentTimeMillis());
        examKwProgress.setType(ExamKwProgressType.findById(type));
        kWService.saveKwProgress(examKwProgress);
        return true;
    }

    // 是否是模拟测试
    @RequestMapping("/is_simulation")
    public Object isSimulation(HttpServletRequest request, HttpServletResponse response)
            throws FileNotFoundException, IOException {
        String examCode = request.getParameter("examCode");
        ExamPlan examPlan = examPlanService.findByExamCode(examCode);
        if (StringUtils.isEmpty(examCode) || examPlan == null) {
            throw ApiException.EXAM_CODE_INVALID;
        }
        if (!ExamStatus.PUBLISHED.equals(examPlan.getExamStatus())) {
            throw ApiException.EXAM_PLAN_UN_PUBLISHED;
        }
        return "1".equals(examPlan.getSimulation().trim());
    }

    protected String invokeDefaultValue(String v) {
        return v != null ? v : "";
    }

    protected String formatDate(Date date) {
        if (date == null) {
            return null;
        }
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
    }

    @ApiOperation(value = "获取检录状态接口")
    @RequestMapping(value = "/registration", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "获取检录状态接口", response = Object.class)})
    public Object getExamStudentRegistration(@ApiParam(value = "考试识别码", required = true) @RequestParam String examCode,
                                             @ApiParam(value = "科目编码", required = true) @RequestParam String courseCode,
                                             @ApiParam(value = "准考证号", required = true) @RequestParam String ticketNumber) {
        ExamStudent examStudent = examStudentService.findStudent(examCode, ticketNumber, courseCode);
        if (Objects.isNull(examStudent)) {
            throw new JkServerException("未找到考生数据");
        }
        return Objects.equals(true, examStudent.getRegistrationStatus());
    }
}
