package com.qmth.themis.business.templete.impl;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

import javax.annotation.Resource;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qmth.themis.business.config.SystemConfig;
import com.qmth.themis.business.constant.SpringContextHolder;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.entity.TEExam;
import com.qmth.themis.business.entity.TEExamCourse;
import com.qmth.themis.business.entity.TEExamPaper;
import com.qmth.themis.business.entity.TOeExamRecord;
import com.qmth.themis.business.enums.EncryptModeEnum;
import com.qmth.themis.business.enums.TaskStatusEnum;
import com.qmth.themis.business.service.TEExamCourseService;
import com.qmth.themis.business.service.TEExamPaperService;
import com.qmth.themis.business.service.TEExamService;
import com.qmth.themis.business.service.TOeExamRecordService;
import com.qmth.themis.business.templete.TaskImportCommon;
import com.qmth.themis.business.templete.TaskImportTemplete;
import com.qmth.themis.business.util.OssUtil;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.FileUtil;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;

import cn.hutool.core.date.DateUtil;

/**
 * @Description: 试卷导入
 * @Author: xiatian
 * @Date: 2020-07-21
 */
@Service("taskExamPaperImportTemplete")
public class TaskExamPaperImportTemplete implements TaskImportTemplete {
	private final static Logger log = LoggerFactory.getLogger(TaskExamPaperImportTemplete.class);
	private SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
	@Resource
	TEExamService teExamService;
	@Resource
	SystemConfig systemConfig;
	@Resource
	TEExamCourseService teExamCourseService;
	@Resource
	TEExamPaperService teExamPaperService;
	
	@Resource
	TOeExamRecordService examRecordService;

	@Override
	@Transactional
	public Result importTask(Map<String, Object> map) throws IOException {
		long start = System.currentTimeMillis();
		TaskImportCommon taskImportCommon = new TaskImportCommon(map);
		taskImportCommon.init();
		StringBuilder result = new StringBuilder();
		String timeFormat = taskImportCommon.getTimeFormat();
		addResult(result, DateUtil.format(new Date(), timeFormat) + "处理开始");
		File file = null;
		try {
			file = taskImportCommon.getUploadFile();
			dispose(file, map, result);
			taskImportCommon.getTbTaskHistory().setSummary("处理成功");
		} catch (Exception e) {
			taskImportCommon.setException(true);
			log.error("处理出错 ", e);
			addResult(result, "处理出错 " + e.getMessage());
			taskImportCommon.getTbTaskHistory().setSummary("处理出错");
		} finally {
			if (file != null) {
				file.delete();
			}
			addResult(result, DateUtil.format(new Date(), timeFormat) + "处理结束");
			long end = System.currentTimeMillis();
			addResult(result, "耗时(秒):" + ((end - start) / 1000));
			taskImportCommon.getTbTaskHistory().setFinishTime(System.currentTimeMillis());
			taskImportCommon.getTbTaskHistory().setStatus(TaskStatusEnum.FINISH);
			taskImportCommon.getTbTaskHistory().setProgress(100.0d);
			taskImportCommon.writeImportResultTxt(result.toString(), file);
		}
		return ResultUtil.ok(Collections.singletonMap("success", true));
	}

	private void dispose(File file, Map<String, Object> map, StringBuilder result) throws IOException {
		Long examId = (Long) map.get("examId");

		TEExam teExam = teExamService.getById(examId);
		if (Objects.isNull(teExam)) {
			throw new BusinessException(ExceptionResultEnum.EXAM_NO);
		}

		String tempDir = SystemConstant.TEMP_FILES_DIR;
		String dir = tempDir + "/" + uuid() + "/";
		File dfile = new File(dir);
		try {
			dfile.mkdirs();
			FileUtil.unZip(dfile, file);
			if (!hasErr(map, dir, examId, dfile, result)) {
				for (File courseDir : dfile.listFiles()) {
					if (courseDir.isDirectory()) {
						disposeCourseDir(dir, teExam, courseDir, map);
					}
				}
			} else {
				throw new BusinessException("校验失败");
			}
		} finally {
			FileUtil.deleteFolder(dir);
		}
	}

	private void disposeCourseDir(String rootDir, TEExam teExam, File courseDir, Map<String, Object> map) {
		String courseCode = courseDir.getName();
		TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(teExam.getId(), courseCode);
		if (course == null) {
			throw new BusinessException("科目编码不存在 " + courseCode);
		}
		File[] childs = courseDir.listFiles();
		for (File paperDir : childs) {
			if (paperDir.isDirectory()) {
				disposePaperDir(rootDir, teExam, course, paperDir, map);
			}
		}
		Boolean objectiveShuffle = (Boolean) map.get("objectiveShuffle");
		Boolean optionShuffle = (Boolean) map.get("optionShuffle");
		Integer audioPlayCount = (Integer) map.get("audioPlayCount");
		if (objectiveShuffle != null) {
			course.setObjectiveShuffle(objectiveShuffle ? 1 : 0);
		}
		if (optionShuffle != null) {
			course.setOptionShuffle(optionShuffle ? 1 : 0);
		}
		if (audioPlayCount != null) {
			course.setAudioPlayCount(audioPlayCount);
		}
		List<TEExamPaper> list = teExamPaperService.findByExamIdAndCourseCode(teExam.getId(), courseCode);
		int hasAnswer = 1;
		for (TEExamPaper p : list) {
			if (StringUtils.isBlank(p.getAnswerPath())) {
				hasAnswer = 0;
				break;
			}
		}
		course.setPaperCount(list.size());
		course.setHasAnswer(hasAnswer);
		int hasAudio = 0;
		for (TEExamPaper p : list) {
			if (p.getHasAudio() != null && p.getHasAudio().intValue() == 1) {
				hasAudio = 1;
				break;
			}
		}
		course.setHasAudio(hasAudio);
		teExamCourseService.saveOrUpdate(course);
		// 设置调卷比例
		teExamPaperService.savePaperWeight(paperWeight(list));
		
		//清除考试科目缓存
		teExamCourseService.deleteExamCourseCacheBean(teExam.getId(), courseCode);
	}

	private Map<Long, Double> paperWeight(List<TEExamPaper> list) {
		BigDecimal b1 = new BigDecimal(1.0);// 基数总数
		BigDecimal b2 = new BigDecimal(list.size());// 份数
		BigDecimal b3 = b1.divide(b2, 2, BigDecimal.ROUND_FLOOR);// 平均数
		BigDecimal b4 = b1.subtract(b3.multiply(b2).setScale(2, BigDecimal.ROUND_FLOOR));// 余数
		Map<Long, Double> map = new HashMap<Long, Double>();
		for (int i = 0; i < list.size(); i++) {
			TEExamPaper p = list.get(i);
			if (i == list.size() - 1) {
				map.put(p.getId(), b3.add(b4).doubleValue());
			} else {
				map.put(p.getId(), b3.doubleValue());
			}
		}
		return map;
	}

	private void disposePaperDir(String rootDir, TEExam teExam, TEExamCourse course, File paperDir,
			Map<String, Object> map) {
		String paperCode = paperDir.getName();
		File[] childs = paperDir.listFiles();

		File paperFile = null;
		File answerFile = null;
		File attachmentDir = null;
		for (File cfile : childs) {
			if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
				paperFile = cfile;
			}
			if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
				answerFile = cfile;
			}
			if ("attachment".equals(cfile.getName()) && cfile.isDirectory()) {
				if (cfile.listFiles() != null && cfile.listFiles().length != 0) {
					attachmentDir = cfile;
				}
			}
		}
		TEExamPaper paper = teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(teExam.getId(),
				course.getCourseCode(), paperCode);
		if (paper == null) {
			paper = new TEExamPaper();
			String createId = map.get("createId").toString();
			JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
			paper.setId(Constants.idGen.next());
			paper.setName(paperJson.getString("name"));
			paper.setTotalScore(paperJson.getDouble("totalScore"));
			paper.setDecryptSecret(RandomStringUtils.randomAlphanumeric(16));
			paper.setEncryptMode(EncryptModeEnum.AUTO);
			paper.setExamId(teExam.getId());
			paper.setCode(paperCode);
			paper.setHasAudio(paperJson.getInteger("hasAudio"));
			paper.setCreateId(Long.parseLong(createId));
			paper.setCourseCode(course.getCourseCode());
			paper.setDecryptVector(RandomStringUtils.randomAlphanumeric(16));
		}
		Boolean processPaper = (Boolean) map.get("processPaper");
		Boolean processAnswer = (Boolean) map.get("processAnswer");
		if (processPaper) {
			disposePaper(rootDir, paper, paperFile, attachmentDir);
			disposeStruct(rootDir, paper, paperFile);
		}
		if (processAnswer) {
			disposeAnswer(rootDir, paper, answerFile);
		}
		Integer audioPlayCount = (Integer) map.get("audioPlayCount");
		paper.setAudioPlayCount(audioPlayCount);
		teExamPaperService.saveOrUpdate(paper);
	}

	private void disposePaper(String rootDir, TEExamPaper paper, File paperFile, File attachmentDir) {
		if (paperFile == null) {
			return;
		}
		File zip = new File(rootDir + uuid() + ".zip");
		List<File> files = new ArrayList<File>();
		files.add(paperFile);
		if (attachmentDir != null) {
			files.add(attachmentDir);
		}
		FileUtil.doZip(zip, files);
		File encryptZip = new File(rootDir + uuid() + ".zip");
		FileUtil.encryptFile(zip, encryptZip, paper.getDecryptSecret(), paper.getDecryptVector());
		String filePath = sdf.format(new Date()) + "/" + uuid() + ".zip";
		paper.setPaperPath(filePath);
		SystemConfig systemConfig = SpringContextHolder.getBean(SystemConfig.class);
		OssUtil.ossUpload(systemConfig.getOssEnv(3), filePath, encryptZip);
	}

	private void disposeStruct(String rootDir, TEExamPaper paper, File paperFile) {
		if (paperFile == null) {
			return;
		}
		JSONObject paperJson = JSONObject.parseObject(FileUtil.readFileContent(paperFile));
		JSONArray details = paperJson.getJSONArray("details");
		for (int i = 0; i < details.size(); i++) {
			JSONArray questions = details.getJSONObject(i).getJSONArray("questions");
			for (int j = 0; j < questions.size(); j++) {
				JSONObject question = questions.getJSONObject(j);
				question.remove("body");
				question.remove("options");
				if (question.getInteger("structType").intValue() == 6) {
					JSONArray subQuestions = question.getJSONArray("subQuestions");
					for (int k = 0; k < subQuestions.size(); k++) {
						JSONObject subquestion = subQuestions.getJSONObject(k);
						subquestion.remove("body");
						subquestion.remove("options");
					}
				}
			}
		}

		File file = new File(rootDir + uuid() + ".json");
		FileUtil.saveAsFile(file.getAbsolutePath(), paperJson.toJSONString());
		String filePath = sdf.format(new Date()) + "/" + uuid() + ".json";
		paper.setStructPath(filePath);
		SystemConfig systemConfig = SpringContextHolder.getBean(SystemConfig.class);
		OssUtil.ossUpload(systemConfig.getOssEnv(3), filePath, file);
	}

	private void disposeAnswer(String rootDir, TEExamPaper paper, File answerFile) {
		if (answerFile == null) {
			return;
		}
//        File encryptFile = new File(rootDir + uuid() + ".json");
//        FileUtil.encryptFile(answerFile, encryptFile, paper.getDecryptSecret(), paper.getDecryptVector());
		String filePath = sdf.format(new Date()) + "/" + uuid() + ".json";
		paper.setAnswerPath(filePath);
		SystemConfig systemConfig = SpringContextHolder.getBean(SystemConfig.class);
		OssUtil.ossUpload(systemConfig.getOssEnv(3), filePath, answerFile);
	}

	private void addResult(StringBuilder result, String msg) {
		result.append(msg).append("\r\n");
	}

	private boolean hasErr(Map<String, Object> map, String rootDir, Long examId, File dfile, StringBuilder result) {
		Boolean processPaper = (Boolean) map.get("processPaper");
		Boolean processAnswer = (Boolean) map.get("processAnswer");
		boolean hasErr = false;
		for (File courseDir : dfile.listFiles()) {// 校验每一个科目
			if (courseDir.isDirectory()) {
				String courseCode = courseDir.getName();
				TEExamCourse course = teExamCourseService.findByExamIdAndCourseCode(examId, courseCode);
				if (course == null) {
					addResult(result, "科目编码不存在 " + courseCode);
					hasErr = true;
					continue;
				}
				File[] childs = courseDir.listFiles();
				if (childs == null || childs.length == 0) {
					addResult(result, "科目 " + courseCode + " 没有需要导入的文件");
					hasErr = true;
					continue;
				}
				for (File paperDir : childs) {// 校验每一套试卷
					if (paperDir.isDirectory()) {
						String paperCode = paperDir.getName();
						File[] paperDirChilds = paperDir.listFiles();
						if (paperDirChilds == null || paperDirChilds.length == 0) {
							addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有需要导入的文件");
							hasErr = true;
							continue;
						}
						if (processPaper) {
							if(checkPaperHasExamRecord(examId, courseCode, paperCode)) {
								addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 已有考试记录，不能再上传试卷");
								hasErr = true;
								continue;
							}
							String[] ps = paperDir.list(new FilenameFilter() {

								@Override
								public boolean accept(File dir, String name) {
									return name.equals("paper.json");
								}
							});
							if (ps == null || ps.length == 0) {
								addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有试卷文件");
								hasErr = true;
								continue;
							}
						}
						if (processAnswer) {
							String[] ps = paperDir.list(new FilenameFilter() {

								@Override
								public boolean accept(File dir, String name) {
									return name.equals("answer.json");
								}
							});
							if (ps == null || ps.length == 0) {
								addResult(result, "科目 " + courseCode + " 试卷 " + paperCode + " 没有答案文件");
								hasErr = true;
								continue;
							}
						}
						try {
							// 校验答案
							checkAnswerFile(rootDir, paperDirChilds, examId, courseCode, paperCode);
						} catch (Exception e) {
							addResult(result, e.getMessage());
							hasErr = true;
							continue;
						}
					}
				}
			}
		}
		return hasErr;
	}

	private boolean checkPaperHasExamRecord(Long examId, String courseCode, String paperCode) {
		TEExamPaper paper=teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode, paperCode);
		if(paper==null) {
			return false;
		}
		TOeExamRecord rd=examRecordService.findOneByPaperId(paper.getId());
		if(rd!=null) {
			return true;
		}
		return false;
	}

	private void checkAnswerFile(String rootDir, File[] paperDirChilds, Long examId, String courseCode,
			String paperCode) {
		File paperFile = null;
		File answerFile = null;
		for (File cfile : paperDirChilds) {// 校验试卷下的答案
			if ("paper.json".equals(cfile.getName()) && cfile.isFile()) {
				paperFile = cfile;
			}
			if ("answer.json".equals(cfile.getName()) && cfile.isFile()) {
				answerFile = cfile;
			}
		}
		if (answerFile != null) {
			if (paperFile == null) {// 没有试卷信息，不能先导入答案
				TEExamPaper paper = teExamPaperService.findByExamIdAndCourseCodeAndPaperCode(examId, courseCode,
						paperCode);
				if (paper == null) {
					throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 没有试卷信息，不能先导入答案");
				}
				String structPath = paper.getStructPath();
				SystemConfig systemConfig = SpringContextHolder.getBean(SystemConfig.class);
				String url = OssUtil.getUrlForPrivateBucket(systemConfig.getOssEnv(3), structPath);
				String name = structPath.substring(structPath.lastIndexOf("/") + 1);
				File structFile = new File(rootDir + name);
				try {
					FileUtil.saveUrlAsFile(url, structFile);
					compareAnswerAndPaper(courseCode, paperCode, answerFile, structFile);
				} finally {
					if (structFile.exists()) {
						structFile.delete();
					}
				}
			} else {
				compareAnswerAndPaper(courseCode, paperCode, answerFile, paperFile);
			}
		}
	}

	private void compareAnswerAndPaper(String courseCode, String paperCode, File answerFile, File structFile) {
		JSONObject answerJson = JSONObject.parseObject(FileUtil.readFileContent(answerFile));
		JSONArray answerdetails = answerJson.getJSONArray("details");
		JSONObject structJson = JSONObject.parseObject(FileUtil.readFileContent(structFile));
		JSONArray structdetails = structJson.getJSONArray("details");
		if (answerdetails.size() != structdetails.size()) {
			throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷大题数量不一致");
		}
		for (int i = 0; i < answerdetails.size(); i++) {
			JSONArray answerdetailquestions = answerdetails.getJSONObject(i).getJSONArray("questions");
			JSONArray structdetailquestions = structdetails.getJSONObject(i).getJSONArray("questions");
			if (answerdetailquestions.size() != structdetailquestions.size()) {
				throw new BusinessException(
						"科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1) + "大题的小题数量不一致");
			}
			for (int j = 0; j < answerdetailquestions.size(); j++) {
				JSONObject answerquestion = answerdetailquestions.getJSONObject(j);
				JSONObject structquestion = structdetailquestions.getJSONObject(j);
				if (structquestion.getInteger("structType").intValue() == 1
						|| structquestion.getInteger("structType").intValue() == 2) {
					JSONArray answer = answerquestion.getJSONArray("answer");
					if (answer == null || answer.size() == 0) {
						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
								+ "大题第" + (j + 1) + "小题没有答案");
					}
				}
				if (structquestion.getInteger("structType").intValue() == 3) {
					Boolean answer = answerquestion.getBoolean("answer");
					if (answer == null) {
						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
								+ "大题第" + (j + 1) + "小题没有答案");
					}
				}
				if (structquestion.getInteger("structType").intValue() == 6) {
					JSONArray answersubQuestions = answerquestion.getJSONArray("subQuestions");
					JSONArray structsubQuestions = structquestion.getJSONArray("subQuestions");
					if (answersubQuestions.size() != structsubQuestions.size()) {
						throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第" + (i + 1)
								+ "大题第" + (j + 1) + "小题子题数量不一致");
					}
					for (int k = 0; k < answersubQuestions.size(); k++) {
						JSONObject answersubquestion = answersubQuestions.getJSONObject(k);
						JSONObject structsubquestion = structsubQuestions.getJSONObject(k);
						if (structsubquestion.getInteger("structType").intValue() == 1
								|| structsubquestion.getInteger("structType").intValue() == 2) {
							JSONArray answer = answersubquestion.getJSONArray("answer");
							if (answer == null || answer.size() == 0) {
								throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第"
										+ (i + 1) + "大题第" + (j + 1) + "小题第" + (k + 1) + "子题没有答案");
							}
						}
						if (structsubquestion.getInteger("structType").intValue() == 3) {
							Boolean answer = answersubquestion.getBoolean("answer");
							if (answer == null) {
								throw new BusinessException("科目 " + courseCode + " 试卷 " + paperCode + " 答案和试卷第"
										+ (i + 1) + "大题第" + (j + 1) + "小题第" + (k + 1) + "子题没有答案");
							}
						}
					}
				}
			}
		}
	}

	private String uuid() {
		return UUID.randomUUID().toString().replaceAll("-", "");
	}
}
