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

import cn.com.qmth.stmms.ms.commons.config.ScoreConfig;
import cn.com.qmth.stmms.ms.commons.utils.SqlUtil;
import cn.com.qmth.stmms.ms.commons.web.PageableDTO;
import cn.com.qmth.stmms.ms.core.cache.ParamCache;
import cn.com.qmth.stmms.ms.core.domain.*;
import cn.com.qmth.stmms.ms.core.domain.enums.TrialEnum;
import cn.com.qmth.stmms.ms.core.domain.task.MarkTask;
import cn.com.qmth.stmms.ms.core.domain.user.MarkUser;
import cn.com.qmth.stmms.ms.core.repository.*;
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.MarkerAssembler;
import cn.com.qmth.stmms.ms.marking.dto.LevelDetailDTO;
import cn.com.qmth.stmms.ms.marking.dto.MarkTaskDTO;
import cn.com.qmth.stmms.ms.marking.service.MarkingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.orm.jpa.vendor.OpenJpaDialect;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.util.*;

/**
 * 评卷任务数据api
 * Created by zhengmin on 2016/10/12.
 */
@RestController
@RequestMapping("api/marktasks")
public class MarkTaskApi {
    private static Logger LOGGER = LoggerFactory.getLogger(MarkTaskApi.class);

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    private MarkingService markingService;

    @Autowired
    private MarkTaskAssembler markTaskAssembler;

    @Autowired
    ExamQuestionRepo examQuestionRepo;

    @Autowired
    PaperRepo paperRepo;

    @Autowired
    MarkUserRepo markUserRepo;

    @Autowired
    ScoreConfig scoreConfig;

    @Autowired
    LevelRepo levelRepo;

    @Autowired
    MarkSubjectRepo markSubjectRepo;

    @Autowired
    SqlUtil sqlUtil;

    /**
     * 评卷员的评卷任务
     *
     * @param markerId   评卷员用户id
     * @param stage      评卷阶段
     * @param level      档位
     * @param sn         密号
     * @param questionId 试题id
     * @return
     */
    @RequestMapping(method = RequestMethod.GET)
    public PageableDTO list(@RequestParam Long markerId,
                            @RequestParam Long workId,
                            @RequestParam MarkStage stage,
                            @RequestParam(required = false) Boolean isSample,
                            @RequestParam(defaultValue = "false") Boolean reject,
                            @RequestParam(required = false) String level,
                            @RequestParam(required = false) String sn,
                            @RequestParam Long questionId,
                            @RequestParam(required = false) String areaCode,
                            Pageable pageable) throws Exception {
        List<MarkTaskDTO> markTaskDTOs = new ArrayList<>();
        MarkUser markUser = markUserRepo.findOne(markerId);
        Specification<MarkTask> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (Objects.nonNull(questionId)) {
                predicates.add(builder.equal(root.get("questionId"), questionId));
            }
            predicates.add(builder.equal(root.get("markerId"), markerId));
            predicates.add(builder.equal(root.get("stage"), stage));
            if (level == null) {
                predicates.add(builder.isNull(root.get("result")));
                if (stage == MarkStage.SCORE) {
                    predicates.add(builder.equal(root.get("paper").get("isShift"), false));
                    predicates.add(builder.equal(root.get("paper").get("isShiftScore"), false));
                }
            } else if (stage == MarkStage.LEVEL) {
                //查询
                predicates.add(builder.equal(root.get("result"), level));
                if(ParamCache.paramMap.get(workId).getLevelShowAllPaper() == 0) {
//                    Long batchNo = paperRepo.findByQuestionId(questionId);
                    Long batchNo = paperRepo.findMaxBatchNoByWorkIdAndSubject(workId, markUser.getSubject());
                    if (!Objects.isNull(batchNo)) {
                        predicates.add(builder.equal(root.get("paper").get("batchNo"), batchNo));
                    }
                }
            } else if (stage == MarkStage.SCORE) {
//                Long batchNo = paperRepo.findScoreBatchNoByQuestionId(questionId);
                Long batchNo = paperRepo.findMaxScoreBatchNoByWorkIdAndSubject(workId, markUser.getSubject());
                predicates.add(builder.equal(root.get("paper").get("level"), level));
                predicates.add(builder.isNotNull(root.get("result")));
                predicates.add(builder.equal(root.get("paper").get("isShift"), false));
                predicates.add(builder.equal(root.get("paper").get("isShiftScore"), false));
                if(ParamCache.paramMap.get(workId).getScoreShowAllPaper() == 1){
                    predicates.add(builder.isNotNull(root.get("paper").get("scoreBatchNo")));
                } else {
                    predicates.add(builder.equal(root.get("paper").get("scoreBatchNo"), batchNo));
                }
            }
            if (isSample != null) {
                predicates.add(builder.equal(root.get("paper").get("isSample"), isSample));
            }
            if (reject != null) {
                predicates.add(builder.equal(root.get("paper").get("isRejected"), reject));
            }
            predicates.add(builder.equal(root.get("paper").get("isMissing"), false));
            predicates.add(builder.equal(root.get("paper").get("active"), true));
            //过滤考区
            if (Objects.nonNull(areaCode)) {
                predicates.add(builder.equal(root.get("paper").get("areaCode"), areaCode));
            }
//            predicates.add(builder.equal(root.get("isActive"), false));
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Sort sort = new Sort( "randomSeq","paper.level", "serialNumber", "randomSeqNew");
        Pageable pageable1 = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Page<MarkTask> markTasks = markTaskRepo.findAll(specification, pageable1);


        if (Objects.isNull(markTasks) || markTasks.getContent().size() == 0) {
            if (Objects.nonNull(isSample) && isSample) {
                ExamQuestion examQuestion = examQuestionRepo.findOne(questionId);
                if (examQuestion.getTest() == TrialEnum.DEFAULT.getId()) {
                    List<Paper> paperList = paperRepo.findSample(level, isSample, false, examQuestion.getSubject().name(), TrialEnum.DEFAULT.getId(), TrialEnum.START_FORMAL.getId(), examQuestion.getAreaCode(), examQuestion.getWorkId());
                    if (Objects.nonNull(paperList) && paperList.size() > 0) {
//                        MarkUser markUser = markUserRepo.findOne(markerId);
                        for (Paper p : paperList) {
                            MarkTask markTask = new MarkTask(markUser, p, stage, 1L);
                            markTaskDTOs.add(markTaskAssembler.toDTO(markTask));
                        }
                    }
                }
            }
        }
        markTasks.getContent().forEach(m -> {
            markTaskDTOs.add(markTaskAssembler.toDTO(m));
        });
        if (stage == MarkStage.SCORE && Objects.isNull(level)) {
//            if (Objects.nonNull(scoreConfig) && Objects.nonNull(scoreConfig.getTaskSort()) && Objects.equals("paper", scoreConfig.getTaskSort())) {
//                Collections.sort(markTaskDTOs, Comparator.comparing(MarkTaskDTO::getPaperId));
//            } else {
//                randomSeqNewSort(markTaskDTOs);
//            }
        } else if (stage == MarkStage.LEVEL && Objects.isNull(level)) {
            randomSeqNewSort(markTaskDTOs);
        }
        return new PageableDTO(markTaskDTOs, markTasks.getTotalElements(), markTasks.getTotalPages(), pageable.getPageNumber());
    }

    /**
     * 评卷员的评卷任务
     *
     * @param markerId   评卷员用户id
     * @param questionId 试题id
     * @return
     */
    @RequestMapping(value = "/shift", method = RequestMethod.GET)
    public PageableDTO list(@RequestParam Long markerId,
                            @RequestParam(required = false) Long workId,
                            @RequestParam(required = false) Boolean isShift,
                            @RequestParam(required = false) Boolean isShiftScore,
                            @RequestParam Long questionId,
                            Pageable pageable) {
        List<MarkTaskDTO> markTaskDTOs = new ArrayList<>();
        Specification<MarkTask> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            predicates.add(builder.equal(root.get("questionId"), questionId));
            predicates.add(builder.equal(root.get("markerId"), markerId));
            if (isShift != null) {
                //查询
                predicates.add(builder.equal(root.get("paper").get("isShift"), isShift));
                if(isShift){
                    predicates.add(builder.equal(root.get("stage"), MarkStage.LEVEL));
                    predicates.add(builder.isNull(root.get("result")));
                }
            }
            if (isShiftScore != null && isShiftScore && !isShift) {
                predicates.add(builder.equal(root.get("paper").get("isShiftScore"), isShiftScore));
                if(isShiftScore){
                    predicates.add(builder.equal(root.get("stage"), MarkStage.SCORE));
                    predicates.add(builder.isNull(root.get("result")));
                }
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Sort sort = new Sort("paper.level", "serialNumber", "randomSeq");
        Pageable pageable1 = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), sort);
        Page<MarkTask> markTasks = markTaskRepo.findAll(specification, pageable1);

        markTasks.getContent().forEach(m -> markTaskDTOs.add(markTaskAssembler.toShiftDTO(m)));
        return new PageableDTO(markTaskDTOs, markTasks.getTotalElements(), markTasks.getTotalPages(), pageable.getPageNumber());
    }

    /**
     * randomSeqNew排序
     *
     * @param markTaskDTOs
     */
    public void randomSeqNewSort(List<MarkTaskDTO> markTaskDTOs) {
        boolean isSort = true;
        for (MarkTaskDTO m : markTaskDTOs) {
            if (Objects.nonNull(m.getRandomSeq())) {
                isSort = false;
                break;
            }
        }
        if (isSort) {
            Collections.sort(markTaskDTOs, Comparator.comparing(MarkTaskDTO::getRandomSeqNew));
        }
    }

    /**
     * 提交评卷任务
     *
     * @param markTask 评卷你任务id
     * @param body     评卷内容
     * @return
     */
    @RequestMapping(value = "{markTask}", method = RequestMethod.PATCH)
    @Transactional
    public ResponseEntity marking(@PathVariable MarkTask markTask, @RequestBody HashMap<String, String> body) {
        MarkStage stage = MarkStage.valueOf(body.get("stage"));
        String result = body.get("result");
        boolean oldRejected = markTask.isRejected();
        if (stage == null || result == null) {
            return new ResponseEntity(HttpStatus.BAD_REQUEST);
        }
        switch (stage) {
            case LEVEL:
                synchronized (this) {
                    markingService.levelMark(markTask, result);
                }
                break;
            case SCORE:
                Integer score = Integer.parseInt(result);
                synchronized (this) {
                    markingService.scoring(markTask, score);
                }
                break;
        }
        return new ResponseEntity(markTaskAssembler.toDTO(markTask, oldRejected), HttpStatus.OK);
    }

    /**
     * 批量提交评卷任务
     *
     * @param body     评卷内容
     * @return
     */
    @RequestMapping(value = "batch", method = RequestMethod.PATCH)
    public ResponseEntity batch(@RequestBody HashMap<String, String> body) {
        MarkStage stage = MarkStage.valueOf(body.get("stage"));
        String markIds = body.get("taskIds");
        String result = body.get("result");
        if(Objects.isNull(markIds)){
            throw new RuntimeException("请选择待评试卷");
        }
        String[] ids = markIds.split(",");
        List<MarkTaskDTO> list = new ArrayList<>();
        for (String id : ids) {
            MarkTask markTask = markTaskRepo.findOne(Long.valueOf(id));
            boolean oldRejected = markTask.isRejected();
            if (stage == null || result == null) {
                return new ResponseEntity(HttpStatus.BAD_REQUEST);
            }
            switch (stage) {
                case LEVEL:
                    synchronized (this) {
                        markingService.levelMark(markTask, result);
                    }
                    break;
                case SCORE:
                    Integer score = Integer.parseInt(result);
                    synchronized (this) {
                        markingService.scoring(markTask, score);
                    }
                    break;
            }
            MarkTaskDTO markTaskDTO = markTaskAssembler.toDTO(markTask, oldRejected);
            list.add(markTaskDTO);
        }

        return new ResponseEntity(list, HttpStatus.OK);
    }

    /**
     * 跳过评卷任务
     *
     * @param markTaskId 评卷你任务id
     * @return
     */
    @RequestMapping(value = "{markTaskId}/skip", method = RequestMethod.POST)
    public ResponseEntity skip(@PathVariable Long markTaskId) {
        MarkTask markTask = markingService.skip(markTaskId);
        return new ResponseEntity(markTaskAssembler.toDTO(markTask), HttpStatus.OK);
    }

    /**
     * 已评试卷回显
     *
     * @param markerId
     * @param stage
     * @param questionId
     * @param areaCode
     * @return
     */
    @RequestMapping(value = "/reviewPaper", method = RequestMethod.GET)
    public List<MarkTaskDTO> reviewPaper(@RequestParam Long markerId,
                                         @RequestParam MarkStage stage,
                                         @RequestParam Long questionId,
                                         @RequestParam(required = false) String areaCode) {
        List<MarkTaskDTO> markTaskDTOs = new ArrayList<>();
        Sort sort = new Sort(Sort.Direction.DESC, "updatedOn");
        Pageable pageable = new PageRequest(0, 5, sort);
        Specification<MarkTask> specification = (root, query, builder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (Objects.nonNull(questionId)) {
                predicates.add(builder.equal(root.get("questionId"), questionId));
            }
            predicates.add(builder.equal(root.get("markerId"), markerId));
            predicates.add(builder.equal(root.get("stage"), stage));
            if (stage == MarkStage.LEVEL) {
                //查询
                Long batchNo = paperRepo.findByQuestionId(questionId);
                if (!Objects.isNull(batchNo)) {
                    predicates.add(builder.equal(root.get("paper").get("batchNo"), batchNo));
                }
                predicates.add(builder.isNotNull(root.get("result")));
                //分档需要过滤标准卷
                predicates.add(builder.equal(root.get("paper").get("isSample"), false));
            } else if (stage == MarkStage.SCORE) {
                Long socreBatchNo = paperRepo.findScoreBatchNoByQuestionId(questionId);
                if (!Objects.isNull(socreBatchNo)) {
                    predicates.add(builder.equal(root.get("paper").get("scoreBatchNo"), socreBatchNo));
                }
                predicates.add(builder.isNotNull(root.get("result")));
            }
            //过滤考区
            if (Objects.nonNull(areaCode)) {
                predicates.add(builder.equal(root.get("paper").get("areaCode"), areaCode));
            }
            return builder.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Page<MarkTask> markTasks = markTaskRepo.findAll(specification, pageable);
        markTasks.getContent().forEach(m -> {
            markTaskDTOs.add(markTaskAssembler.toDTO(m));
        });
        return markTaskDTOs;
    }


    /**
     * 分档详情
     *
     * @param workId
     * @param subject
     * @param questionId
     * @return
     */
    @RequestMapping(value = "/levelStatDetail", method = RequestMethod.GET)
    public List<LevelDetailDTO> levelStatDetail(@RequestParam Long workId,
                                                @RequestParam Subject subject,
                                                @RequestParam(required = false) Long questionId) {

        List<Level> levels = levelRepo.findByWorkIdOrderByCode(workId);
        List<Paper> papers;
        if (questionId == null) {
            papers = paperRepo.findByWorkIdAndSubjectAndIsMissingAndTest(workId, subject, false, TrialEnum.DEFAULT.getId());
        } else {
            papers = paperRepo.findByworkIdAndSubjectAndQuestionIdAndIsMissingAndTest(workId, subject, questionId, false, TrialEnum.DEFAULT.getId());
        }
        MarkSubject markSubject = markSubjectRepo.findOne(workId + "-" + subject.name());

        List<LevelDetailDTO> list = new ArrayList<>();
        for (Level level : levels) {
            LevelDetailDTO levelDetailDTO = new LevelDetailDTO();
            levelDetailDTO.setWorkId(workId);
            levelDetailDTO.setSubjectName(markSubject.getName());
            //档位
            levelDetailDTO.setCode(level.getCode());
            levelDetailDTO.setMinScore(level.getMinScore());
            levelDetailDTO.setMaxScore(level.getMaxScore());
            //数量
            long count = papers.stream().filter(m -> Objects.nonNull(m.getLevel()) && Objects.equals(level.getCode(), m.getLevel())).count();
            levelDetailDTO.setLevelCount((int) count);
            BigDecimal prop = papers.size() == 0 ? new BigDecimal("0") : new BigDecimal(String.valueOf(count * 100)).divide(new BigDecimal(String.valueOf(papers.size())), 2, BigDecimal.ROUND_HALF_UP);
            //占比
            levelDetailDTO.setLevelProp(prop.doubleValue());
            //预设占比
            levelDetailDTO.setExamLevelProp(level.getPt());
            //差值
            BigDecimal diff = prop.subtract(new BigDecimal(String.valueOf(level.getPt())));
            levelDetailDTO.setDiffProp(diff.doubleValue());
            //累计数量
            int sumCount = (int) (list.stream().mapToInt(LevelDetailDTO::getLevelCount).sum() + count);
            levelDetailDTO.setCumulateCount(sumCount);
            //累计占比
            BigDecimal cumulateProp = papers.size() == 0 ? new BigDecimal("0") : new BigDecimal(String.valueOf(sumCount * 100)).divide(new BigDecimal(String.valueOf(papers.size())), 2, BigDecimal.ROUND_HALF_UP);
            levelDetailDTO.setCumulateProp(cumulateProp.doubleValue());

            //调整
            int adjustment = new BigDecimal(String.valueOf(papers.size())).multiply(diff.divide(new BigDecimal("100"))).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            levelDetailDTO.setAdjustmentCount(adjustment);

            list.add(levelDetailDTO);
        }
        return list;
    }
}
