package cn.com.qmth.stmms.ms.marking.api;

import cn.com.qmth.stmms.ms.commons.config.SystemConfig;
import cn.com.qmth.stmms.ms.commons.web.PageableDTO;
import cn.com.qmth.stmms.ms.core.domain.ExamQuestion;
import cn.com.qmth.stmms.ms.core.domain.MarkStage;
import cn.com.qmth.stmms.ms.core.domain.MarkSubject;
import cn.com.qmth.stmms.ms.core.domain.Paper;
import cn.com.qmth.stmms.ms.core.domain.task.MarkTask;
import cn.com.qmth.stmms.ms.core.repository.ExamQuestionRepo;
import cn.com.qmth.stmms.ms.core.repository.MarkSubjectRepo;
import cn.com.qmth.stmms.ms.core.repository.MarkTaskRepo;
import cn.com.qmth.stmms.ms.core.repository.PaperRepo;
import cn.com.qmth.stmms.ms.core.vo.Subject;
import cn.com.qmth.stmms.ms.marking.assembler.MarkTaskAssembler;
import cn.com.qmth.stmms.ms.marking.assembler.PaperAssembler;
import cn.com.qmth.stmms.ms.marking.dto.MarkTaskDTO;
import cn.com.qmth.stmms.ms.marking.dto.PaperDTO;
import cn.com.qmth.stmms.ms.marking.service.MarkingService;
import javafx.beans.binding.ObjectExpression;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 试卷相关api
 * Created by zhengmin on 2016/10/10.
 */
@RestController
@RequestMapping("api/papers")
public class PaperApi {

    @Autowired
    private PaperAssembler paperAssembler;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    private MarkSubjectRepo markSubjectRepo;

    @Autowired
    private MarkTaskAssembler markTaskAssembler;

    @Autowired
    private MarkingService markingService;

    @Autowired
    ExamQuestionRepo examQuestionRepo;

    @Autowired
    SystemConfig systemConfig;

    /**
     * 单个试卷信息
     *
     * @param paperId 试卷id
     * @return
     */
    @RequestMapping(value = "{paperId}", method = RequestMethod.GET)
    public PaperDTO get(@PathVariable Long paperId) {
        return paperAssembler.toDTO(paperRepo.findOne(paperId));
    }

    /**
     * 单个试卷信息
     *
     * @param questionId 试题id
     * @param sn         密号
     * @param examNumber 准考证号
     * @return
     */
    @RequestMapping(value = "/one", method = RequestMethod.GET)
    public PaperDTO getOne(@RequestParam Long questionId,
                           @RequestParam(required = false) String sn,
                           @RequestParam(required = false) String examNumber) {
        PaperDTO paperDTO = null;
        Specification<Paper> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("questionId"), questionId));
            if (sn != null) {
                predicates.add(builder.equal(root.get("secretNumber"), sn));
            }
            if (examNumber != null) {
                predicates.add(builder.equal(root.get("examNumber"), examNumber));
            }

            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Optional<Paper> paperOptional = paperRepo.findAll(specification, new PageRequest(0, 1))
                .getContent().stream().findFirst();
        if (paperOptional.isPresent()) {
            paperDTO = paperAssembler.toDTO(paperOptional.get());
        }
        return paperDTO;
    }

    @RequestMapping(value = "search/byTaskSecretNumber", method = RequestMethod.GET)
    public PaperDTO getByTaskSecretNumber(@RequestParam Long questionId,
                                          @RequestParam String sn) {
        PaperDTO paperDTO = null;
        Specification<MarkTask> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("paper").get("questionId"), questionId));
            if (sn != null) {
                predicates.add(builder.equal(root.get("secretNumber"), sn));
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Optional<MarkTask> taskOptional = markTaskRepo.findAll(specification, new PageRequest(0, 1))
                .getContent().stream().findFirst();
        if (taskOptional.isPresent()) {
            paperDTO = paperAssembler.toDTO(taskOptional.get().getPaper());
        }
        return paperDTO;
    }

    @RequestMapping(value = "listByQuestion", method = RequestMethod.GET)
    public PageableDTO listByQuerion(@RequestParam Long workId,
                                     @RequestParam(required = false) String areaCode,
                                     @RequestParam Subject subject,
                                     @RequestParam(required = false) Long startNumber,
                                     @RequestParam(required = false) Long endNumber,
                                     @RequestParam(required = false) Boolean isManual,
                                     @RequestParam(required = false) Boolean missing,
                                     @RequestParam(required = false) String studentName,
                                     @RequestParam(required = false) String sortBy,
                                     Pageable pageable) {
        Sort sort = null;
        if (Objects.equals("1", sortBy)) {
            sort = new Sort(Sort.Direction.DESC, "uploadedOn");
        }
        if (Objects.equals("2", sortBy)) {
            sort = new Sort(Sort.Direction.ASC, "examNumber");
        }
        Pageable pageable1 = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Specification<Paper> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("workId"), workId));
            if (areaCode != null && areaCode != "") {
                predicates.add(builder.equal(root.get("areaCode"), areaCode));
            }
            if (subject != null) {
                predicates.add(builder.equal(root.get("subject"), subject));
            }
            if (startNumber != null && endNumber != null) {
                predicates.add(builder.between(root.get("examNumber"), startNumber, endNumber));
            } else if (startNumber != null && endNumber == null) {
                predicates.add(builder.equal(root.get("examNumber"), startNumber));
            } else if (startNumber == null && endNumber != null) {
                predicates.add(builder.equal(root.get("examNumber"), endNumber));
            }
            if (isManual != null) {
                predicates.add(builder.equal(root.get("isManual"), isManual));
            }
            if (missing != null) {
                predicates.add(builder.equal(root.get("isMissing"), missing));
            }
            //考生姓名
            if (studentName != null && studentName != "") {
                predicates.add(builder.equal(root.get("studentName"), studentName));
            }
            predicates.add(builder.equal(root.get("test"), 0));
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        List<PaperDTO> paperDTOs = new ArrayList<>();
        Page<Paper> paperPage = paperRepo.findAll(specification, pageable1);
        paperPage.getContent().forEach(p -> {
            paperDTOs.add(paperAssembler.toDTOForPaperManager(p, isManual));
        });
        return new PageableDTO(paperDTOs, paperPage.getTotalElements(), paperPage.getTotalPages(), pageable1.getPageNumber());
    }

    /**
     * 试卷列表
     *
     * @param questionId 试题id
     * @param level      档位，如果为空，则测序待评
     * @param arbi       是否仲裁
     * @param reject     是否打回
     * @param pageable   分页参数
     * @return
     */
    @RequestMapping(method = RequestMethod.GET)
    public PageableDTO list(@RequestParam Long questionId,
                            @RequestParam(required = false) String level,
                            @RequestParam(required = false) Boolean isSample,
                            @RequestParam(required = false) Boolean markedLogic,
                            @RequestParam(required = false) Boolean arbi,
                            @RequestParam(required = false) Boolean reject,
                            @RequestParam(defaultValue = "false") Boolean isScore,//打分阶段该档位已打分数
                            Pageable pageable) {
        //查询最大批次号
//        Long batchNo = paperRepo.findByQuestionId(questionId);
        Specification<Paper> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("questionId"), questionId));
            //isScore为true时，为科组长打分详情页面数据查询条件
            if(isScore) {
                //level为null时，查询待评数量
                if (Objects.isNull(level)) {
                    predicates.add(builder.isNotNull(root.get("level")));
                    predicates.add(builder.isNull(root.get("score")));
                    predicates.add(builder.isNotNull(root.get("scoreBatchNo")));
                } else {
                    predicates.add(builder.equal(root.get("level"), level));
                    predicates.add(builder.isNotNull(root.get("score")));
                }
            } else {
                if (Objects.isNull(level)) {
                    predicates.add(builder.isNull(root.get("level")));
                    //查询当前批次号数据
//                predicates.add(builder.equal(root.get("batchNo"), batchNo));
                } else {
                    predicates.add(builder.equal(root.get("level"), level));
                }
            }
            if (arbi != null) {
                predicates.add(builder.equal(root.get("isArbitrated"), arbi));
            }
            if (reject != null) {
                predicates.add(builder.equal(root.get("isRejected"), reject));
            }
            if (isSample != null) {
                predicates.add(builder.equal(root.get("isSample"), isSample));
            }
            if (markedLogic != null) {
                predicates.add(builder.equal(root.get("markedLogic"), markedLogic));
            }
            //空白卷不展示
            predicates.add(builder.equal(root.get("isMissing"), false));
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Page<Paper> papers = paperRepo.findAll(specification, pageable);
        List<PaperDTO> paperDTOs = new ArrayList<>();
        papers.forEach(p -> {
            paperDTOs.add(paperAssembler.toDTO(p));
        });
        paperDTOs.sort(Comparator.comparingInt(PaperDTO::getSortSum));
        return new PageableDTO(paperDTOs, papers.getTotalElements(), papers.getTotalPages(), pageable.getPageNumber());
    }

    /**
     * 试卷处理。提交评卷
     *
     * @param paper 试卷id
     * @param body  试卷信息
     * @return
     */
    @RequestMapping(value = "{paper}", method = RequestMethod.PATCH)
    @Transactional
    public ResponseEntity marking(@PathVariable Paper paper, @RequestBody HashMap<String, String> body) {
        String action = body.get("action");
        String level = body.get("level");
        String tagged = body.get("tagged");
        //需要打回的评卷员
        String ranges = body.get("range");
        if (action != null && level != null) {
            if (action.equals("leveling")) {
                markingService.levelMarkPaper(paper, level, false);
            } else if (action.equals("sampling")) {
                markingService.levelMarkPaper(paper, level, true);
            } else if (action.equals("reject")) {
                markingService.reject(paper, level, ranges);
            }
        }
        if (tagged != null) {
            boolean isTagged = paper.isTagged();
            paper.setTagged(!isTagged);
            paperRepo.save(paper);
        }
        return new ResponseEntity(paperAssembler.toDTO(paper), HttpStatus.OK);
    }

    /**
     * 批量试卷处理。提交评卷
     *
     * @param body 试卷信息
     * @return
     */
    @RequestMapping(value = "batch", method = RequestMethod.PATCH)
    @Transactional
    public ResponseEntity marking(@RequestBody HashMap<String, String> body) {
        String action = body.get("action");
        String level = body.get("level");
        String tagged = body.get("tagged");
        String paperIds = body.get("paperIds");
        if (paperIds.length() > 0) {
            List<PaperDTO> list = new ArrayList<>();
            String[] ids = paperIds.split(",");
            for (String id : ids) {
                Paper paper = paperRepo.findOne(Long.valueOf(id));
                //需要打回的评卷员
                String ranges = body.get("range");
                if (action != null && level != null) {
                    if (action.equals("leveling")) {
                        markingService.levelMarkPaper(paper, level, false);
                    } else if (action.equals("sampling")) {
                        markingService.levelMarkPaper(paper, level, true);
                    } else if (action.equals("reject")) {
                        markingService.reject(paper, level, ranges);
                    }
                }
                if (tagged != null) {
                    boolean isTagged = paper.isTagged();
                    paper.setTagged(!isTagged);
                    paperRepo.save(paper);
                }
                PaperDTO paperDTO = paperAssembler.toDTO(paper);
                list.add(paperDTO);
            }
            return new ResponseEntity(list, HttpStatus.OK);
        }
        return null;
    }

    /**
     * 标记试卷
     *
     * @param paper 试卷id
     * @return
     */
    @RequestMapping(value = "{paper}/tag", method = RequestMethod.PATCH)
    @Transactional
    public Paper tagging(@PathVariable Paper paper) {
        boolean isTagged = paper.isTagged();
        paper.setTagged(!isTagged);
        paperRepo.save(paper);
        return paper;
    }

    /**
     * 该试卷的评卷记录
     *
     * @param paperId 试卷id
     * @param stage   评卷阶段
     * @return
     */
    @RequestMapping(value = "{paperId}/marktasks", method = RequestMethod.GET)
    public List<MarkTaskDTO> markTasks(@PathVariable Long paperId, @RequestParam MarkStage stage) {
        List<MarkTaskDTO> markTaskDTOs = new ArrayList<>();
        markTaskRepo.findByPaperIdAndStage(paperId, stage).forEach(o -> {
            markTaskDTOs.add(markTaskAssembler.toDTO(o));
        });
        Collections.sort(markTaskDTOs, Comparator.comparing(MarkTaskDTO::getMarker));
        return markTaskDTOs;
    }

    /**
     * 考生成绩查询
     *
     * @param workId
     * @param questionId
     * @param subject
     * @param number
     * @param studentName
     * @return
     */
    @RequestMapping(value = "/studentScore", method = RequestMethod.GET)
    public List<Map> markTasks(@RequestParam Long workId,
                               @RequestParam(required = false) Long questionId,
                               @RequestParam(required = false) Subject subject,
                               @RequestParam(required = false) String type,
                               @RequestParam(required = false) String number,
                               @RequestParam(required = false) String studentName) {
        Specification<Paper> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (!StringUtils.isEmpty(workId)) {
                predicates.add(builder.equal(root.get("workId"), workId));
            }
            if (!StringUtils.isEmpty(questionId)) {
                predicates.add(builder.equal(root.get("questionId"), questionId));
            }
            if (!StringUtils.isEmpty(subject)) {
                predicates.add(builder.equal(root.get("subject"), subject));
            }
            if (!StringUtils.isEmpty(type) && !StringUtils.isEmpty(number)) {
                if (Objects.equals(type, "examNumber")) {
                    predicates.add(builder.equal(root.get("examNumber"), number));
                } else if (Objects.equals(type, "paper")) {
                    predicates.add(builder.equal(root.get("secretNumber"), number));
                } else if (Objects.equals(type, "task")) {
                    List<MarkTask> markTasks = markTaskRepo.findByWorkIdAndSecretNumber(workId, number);
                    if (markTasks == null || markTasks.size() == 0) {
                        predicates.add(builder.equal(root.get("id"), null));
                    } else {
                        predicates.add(builder.equal(root.get("id"), markTasks.get(0).getPaper().getId()));
                    }
                }

            }
            if (!StringUtils.isEmpty(studentName)) {
                predicates.add(builder.equal(root.get("studentName"), studentName));
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        List<Paper> papers = paperRepo.findAll(specification);
        //根据考生分组
        Map<String, List<Paper>> groupPaper = papers.stream().collect(Collectors.groupingBy(p -> p.getExamNumber() + ":" + p.getStudentName()));
        List<Map> mapList = new ArrayList<>();
        for (Map.Entry<String, List<Paper>> entry : groupPaper.entrySet()) {
            Map map = new HashMap();
            String key = entry.getKey();
            List<Paper> paperList = entry.getValue();
            map.put("examNumber", key.split(":")[0]);
            map.put("name", key.split(":")[1]);
            double sumScore = paperList.stream().filter(m -> Objects.nonNull(m.getScore())).mapToDouble(Paper::getScore).sum();
            map.put("sumScore", Optional.ofNullable(sumScore).orElse(0D).intValue());
            List<PaperDTO> paperDTOs = new ArrayList<>();
            paperList.forEach(p -> paperDTOs.add(paperAssembler.toDTO(p)));
            map.put("scores", paperDTOs);
            mapList.add(map);
        }
        return mapList;
    }

    /**
     * 上传试题列表
     *
     * @param workId
     * @throws Exception
     */
    @RequestMapping(value = "/{workId}/listUploadPaper", method = {RequestMethod.GET})
    public List<Map> uploadPaperFile(@PathVariable Long workId) {
        List<Object[]> questions = examQuestionRepo.findByWorkId(workId);
        List<MarkSubject> subjects = markSubjectRepo.findAllByWorkId(workId);
        List<Map> mapList = new ArrayList<>();
        for (Object[] question : questions) {
            Map map = new HashMap();
            map.put("questionId", question[0]);
            map.put("questionName", question[1]);
            List<Map> subjectList = new ArrayList<>();
            for (MarkSubject subject : subjects) {
                //科目信息
                Map subjectMap = new HashMap();
                subjectMap.put("subject", subject.getSubject().name());
                subjectMap.put("subjectName", subject.getName());
                subjectMap.put("enable", subject.isEnable());
                //上传路径，已有上传，取文件路径，没有为null
                String subjectUrl = null;
                String path = systemConfig.getLocalhostPath() + File.separator + "files" + File.separator + workId + File.separator + question[0] + File.separator + subject.getSubject().name();
                File pathFile = new File(path);
                if (pathFile.exists()) {
                    File[] file = pathFile.listFiles();
                    if (file.length > 0) {
                        subjectUrl = file[0].getPath();
                    }
                }
                subjectMap.put("url", Objects.isNull(subjectUrl) ? null : systemConfig.getFilesUrl() + File.separator + subjectUrl);
                subjectList.add(subjectMap);
            }
            map.put("subjects", subjectList);
            mapList.add(map);
        }
        return mapList;
    }


    /**
     * 上传试卷
     *
     * @param workId
     * @param questionId
     * @param subject
     * @param file
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/{workId}/{questionId}/{subject}/uploadPaperFile", method = {RequestMethod.POST})
    public void uploadPaperFile(@PathVariable Long workId,
                                @PathVariable Long questionId,
                                @PathVariable Subject subject,
                                @RequestParam MultipartFile file,
                                HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (Objects.isNull(file)) {
            throw new Exception("file文件不能为空");
        }
        int temp = file.getOriginalFilename().indexOf(".");
        String format = file.getOriginalFilename().substring(temp);
        if (Objects.nonNull(format) && !(format.equalsIgnoreCase(".jpg")
                || format.equalsIgnoreCase(".jpeg")
                || format.equalsIgnoreCase(".png")
                || format.equalsIgnoreCase(".pdf"))) {
            throw new Exception("file文件格式只能为jpg,jpeg,png,pdf");
        }
        InputStream inputStream = file.getInputStream();
        //保存
        String savePath = systemConfig.getLocalhostPath() + File.separator + "files" + File.separator + workId + File.separator + questionId + File.separator + subject.name();
        File fileSave = new File(savePath);
        if (!fileSave.exists()) {
            fileSave.mkdirs();
        }
        //删除原文件
        File[] oldFiles = fileSave.listFiles();
        if (oldFiles.length > 0) {
            for (File oldFile : oldFiles) {
                if (oldFile.exists()) {
                    oldFile.delete();
                }
            }
        }
        File outSheetFile = new File(savePath + File.separator + DigestUtils.md5Hex(workId + "-" + questionId + "-" + subject.name()) + format);
        OutputStream outputSheetStream = new FileOutputStream(outSheetFile);
        int index = 0;
        byte[] bytes = new byte[1024 * 8];
        while ((index = inputStream.read(bytes)) != -1) {
            outputSheetStream.write(bytes, 0, index);
        }
        inputStream.close();
        outputSheetStream.flush();
        outputSheetStream.close();

        String md5 = request.getHeader("md5");
        if (Objects.isNull(md5)) {
            throw new Exception("request的md5为空");
        }
        FileInputStream in = new FileInputStream(outSheetFile);
        String sliceMD5 = DigestUtils.md5Hex(in);
        if (!md5.equalsIgnoreCase(sliceMD5)) {
            throw new Exception("图片md5值不一致");
        }
        in.close();
    }

    /**
     * 查看试题
     *
     * @param workId
     * @throws Exception
     */
    @RequestMapping(value = "/{workId}/{subject}/viewUploadPaper", method = {RequestMethod.GET})
    public List<Map> viewPaperFile(@PathVariable Long workId, @PathVariable Subject subject) {
        List<ExamQuestion> questions = examQuestionRepo.findByWorkIdAndSubject(workId, subject);
        List<Map> mapList = new ArrayList<>();
        for (ExamQuestion question : questions) {
            Map map = new HashMap();
            map.put("questionId", question.getAreaCode());
            map.put("questionName", question.getAreaName());
            //科目信息
            MarkSubject markSubject = markSubjectRepo.findOne(workId + "-" + subject.name());
            map.put("subject", markSubject.getSubject().name());
            map.put("subjectName", markSubject.getName());
            //上传路径，已有上传，取文件路径，没有为null
            String subjectUrl = null;
            String path = systemConfig.getLocalhostPath() + File.separator + "files" + File.separator + workId + File.separator + question.getAreaCode() + File.separator + markSubject.getSubject().name();
            File pathFile = new File(path);
            if (pathFile.exists()) {
                File[] file = pathFile.listFiles();
                if (file.length > 0) {
                    subjectUrl = file[0].getPath();
                }
            }
            map.put("url", systemConfig.getFilesUrl() + File.separator + subjectUrl);
            mapList.add(map);
        }
        return mapList;
    }
}
