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

import cn.com.qmth.stmms.ms.commons.config.LevelConfig;
import cn.com.qmth.stmms.ms.commons.constant.ArbitrateCallback;
import cn.com.qmth.stmms.ms.commons.constant.ArbitrateResult;
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.task.MarkTask;
import cn.com.qmth.stmms.ms.core.domain.user.MarkUser;
import cn.com.qmth.stmms.ms.core.domain.user.Role;
import cn.com.qmth.stmms.ms.core.repository.*;
import cn.com.qmth.stmms.ms.marking.config.MarkingConfig;
import cn.com.qmth.stmms.ms.marking.service.arbitration.ArbitrationService;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 评卷，分档仲裁及打分服务
 * Created by zhengmin on 2016/9/23.
 */
@Service
public class MarkingService {

    private static final Logger LOG = LoggerFactory.getLogger(MarkingService.class);
    @Autowired
    private MarkingConfig markingConfig;

    @Autowired
    private DetermineLevelService determineLevelService;

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    private MarkUserRepo markUserRepo;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private LevelRepo levelRepo;

    @Autowired
    MarkingService markingService;

    @Autowired
    MarkLogRepo markLogRepo;

    @Autowired
    WorkRepo workRepo;

    @Autowired
    LevelConfig levelConfig;

    @Autowired
    ArbitrateCallback arbitrateCallback;

    @Resource
    MarkTaskJobRepo markTaskJobRepo;

    /**
     * 提交分档。如果每个评卷都提交了，则进行仲裁判定，如果没有触发仲裁就定档
     *
     * @param markTask
     * @param levelCode
     * @return
     */
    @Transactional
    public MarkTask levelMark(MarkTask markTask, String levelCode) {
        long start = System.currentTimeMillis();
        LOG.info("提交分档:{}", start);
        //TODO 回评
        //boolean isMarked = markTask.getLevel() == null;

        //如果已经由科组长定档，则不做处理
        Paper paper = markTask.getPaper();
        if (paper.isMarkByLeader() && Objects.nonNull(paper.getLevel())) {
            return markTask;
        }
        /**
         阀值判断

         //控制分档比例
         List<Level> levels = levelRepo.findByWorkId(markTask.getWorkId());
         Level targetLevel = null;
         for (Level l : levels) {
         if (l.getCode().equals(levelCode)) {
         targetLevel = l;
         break;
         }
         }
         int pt = targetLevel.getPt();
         int count = markTaskRepo.countByWorkIdAndSubjectAndMarkerIdAndStageAndResult(markTask.getWorkId(), markTask.getSubject(), markTask.getMarkerId(), markTask.getStage(), levelCode);
         int total = markTaskRepo.countByWorkIdAndSubjectAndMarkerIdAndStage(markTask.getWorkId(), markTask.getSubject(), markTask.getMarkerId(), markTask.getStage());

         if ((count + 1.0D) / total * 1.0D > pt / 100.D) {
         throw new RuntimeException(levelCode + "档已满，占比总阀值已达" + pt + "%。");
         }
         //判断考点阀值是否已满
         int kdpt = targetLevel.getKdpt();
         int kdcount = markTaskRepo.countByWorkIdAndSubjectAndMarkerIdAndStageAndQuestionIdAndResult(markTask.getWorkId(), markTask.getSubject(), markTask.getMarkerId(), markTask.getStage(), markTask.getQuestionId(), levelCode);
         int kdtotal = markTaskRepo.countByWorkIdAndSubjectAndMarkerIdAndStageAndQuestionId(markTask.getWorkId(), markTask.getSubject(), markTask.getMarkerId(), markTask.getStage(), markTask.getQuestionId());
         if ((kdcount + 1.0D) / kdtotal * 1.0D > kdpt / 100.D) {
         throw new RuntimeException(levelCode + "档已满，考点阀值已达" + kdpt + "%。");
         }
         */

        MarkUser maker = markUserRepo.findOne(markTask.getMarkerId());
        Level level = levelRepo.findByWorkIdAndCode(markTask.getWorkId(), levelCode);

        markTask.setResult(levelCode);
        markTask.setLevelValue(level.getLevelValue());
        markTask.setRejected(false);
        markTask.setUpdatedOn(new Date());
        markTask.setMarkerName(maker.getName());
        //激活试卷
//        markTask.setActive(true);

        markTaskRepo.save(markTask);

        //判断该任务的试卷是否所有评卷员都评完，要进行仲裁判定或定档

        List<MarkTask> markTasks = markTaskRepo.findByPaperIdAndStage(paper.getId(), markTask.getStage());
        long leftCount = markTasks.stream().filter(i -> i.getResult() == null).count();
        if (leftCount == 0) {
            MarkTask[] sources = new MarkTask[markTasks.size()];
            //定档
            Integer majority = Optional.ofNullable(ParamCache.paramMap.get(markTask.getWorkId()).getMajority()).orElse(1);
            DeterResult determine = determineLevelService.determine(majority.intValue() == 1 ? true : false, markTasks.toArray(sources));
            //1.过半定档
            if (Objects.nonNull(determine) && Objects.nonNull(determine.getResult()) && determine.getDeterType() == DetermineLevelService.DeterType.MAJORITY) {
                paper.determineLevel(determine.getResult());
                paper.setMarkedLogic(false);
                //档位落差值日志
                levelsLog(markTasks, paper);
            } else {
                int[] values = markTasks.stream().mapToInt(MarkTask::getLevelValue).toArray();
                Integer deviation = Optional.ofNullable(ParamCache.paramMap.get(markTask.getWorkId()).getDeviation()).orElse(3);
                boolean result = ArbitrationService.arbitrate(deviation.intValue(), values);
                //2.最大落差超过仲裁档位,则仲裁打回
                if (result) {
                    //仲裁
                    paper.arbitrate();
                }
                //3.最大落差未超过仲裁档位,则权重计算定档
                else if (Objects.nonNull(determine) && Objects.nonNull(determine.getResult()) && determine.getDeterType() == DetermineLevelService.DeterType.WEIGHT) {
                    paper.determineLevel(determine.getResult());
                    paper.setMarkedLogic(true);
                    //档位落差值日志
                    levelsLog(markTasks, paper);
                }
            }
            //改档后重新分档，状态改为false
            paper.setShift(false);
            //重新分档，打回科组长打回状态改为false
            paper.setRejectedByLeader(false);
            paperRepo.save(paper);
        }
        long end = System.currentTimeMillis();
        LOG.info("提交分档耗时：{}", (end - start) / 1000 + "s");
        return markTask;
    }

    /**
     * 档位落差值计算日志
     *
     * @param markTasks
     * @param paper
     */
    @Transactional
    public void levelsLog(List<MarkTask> markTasks, Paper paper) {
        //20191108wangliang加入档位落差值计算
        LOG.info("this:{} levelsLog异步回调进来了", this);
        JSONObject js = new JSONObject();
        js.put("paperId", paper.getId());
        js.put("type", "levelsLog");
        MarkTaskJob markTaskJob = new MarkTaskJob(js.toJSONString(), false, paper.getWorkId());
        levelsLog(markTasks, paper, markTaskJob);
        markTaskJobRepo.save(markTaskJob);
        LOG.info("this:{} levelsLog异步回调结束了", this);
    }

    /**
     * 档位落差值计算日志
     *
     * @param markTasks
     * @param paper
     * @param markTaskJob
     */
    public void levelsLog(List<MarkTask> markTasks, Paper paper, MarkTaskJob markTaskJob) {
//        List<MarkTask> result = markTasks.stream().filter(x -> Objects.isNull(x.getResult())).collect(Collectors.toList());
//        if (Objects.nonNull(result) && result.size() > 0) {
//            LOG.info("数据有误，未完成评卷任务");
//            markTaskJobRepo.updateMarkTaskJobByVersion(markTaskJob.getId(), markTaskJob.getVersion());
//            return;
//        }
        Map<Long, String> levelsMap = markTasks.stream().collect(Collectors.toMap(MarkTask::getMarkerId, o -> o.getResult().toUpperCase()));
        arbitrateCallback.judge(levelsMap, paper.getLevel(), new ArbitrateResult() {
            @Override
            public void callback(List<ArbitrateCallback.Distance> list) {
                LOG.info("this:{} callback levelsLog异步回调进来了", this);
                String operResult = "-";
                for (ArbitrateCallback.Distance d : list) {
                    for (MarkTask m : markTasks) {
                        if (Objects.equals(String.valueOf(d.getC()), m.getResult()) && d.getMarkId().longValue() == m.getMarkerId().longValue()) {
                            MarkLog markLogPrev = markLogRepo.findTopByCreateUserIdAndOperTypeAndSubjectAndExamNumberOrderByCreateTimeDesc(m.getMarkerId(), MarkLogOperType.LEVEl_DIFFERENCE.getId(), m.getSubject().toString(), paper.getExamNumber());
                            JSONObject jsonObject = new JSONObject();
                            jsonObject.put("task", m.getResult());
                            jsonObject.put("paper", paper.getLevel());
                            if (Objects.isNull(markLogPrev)) {//新建日志
                                //加入档位落差值日志
                                Work work = workRepo.findOne(paper.getWorkId());
                                MarkLog markLog = new MarkLog(m.getMarkerId(), m.getMarkerName(), Role.MARKER, m.getSubject(), paper.getExamNumber(), paper.getStudentName(), MarkLogOperType.LEVEl_DIFFERENCE.getId(), paper.getWorkId(), paper.getId(), m.getStage(), operResult, String.valueOf(d.getDistance()), jsonObject.toJSONString(), work.getName());
                                markLogRepo.save(markLog);
                            } else {//更新日志
                                operResult = Optional.ofNullable(markLogPrev.getOperDataAfter()).orElse("-");
                                markLogPrev.setOperDataBefore(operResult);
                                markLogPrev.setOperDataAfter(String.valueOf(d.getDistance()));
                                markLogPrev.setRemark(jsonObject.toJSONString());
                                markLogRepo.save(markLogPrev);
                            }
                            //档位偏差
                            MarkLog markLogDevi = markLogRepo.findTopByCreateUserIdAndOperTypeAndSubjectAndExamNumberOrderByCreateTimeDesc(m.getMarkerId(), MarkLogOperType.LEVEl_DEVIATION.getId(), m.getSubject().toString(), paper.getExamNumber());
                            if (Objects.isNull(markLogDevi)) {//新建日志
                                //加入档位偏差值日志
                                Work work = workRepo.findOne(paper.getWorkId());
                                MarkLog markLog = new MarkLog(m.getMarkerId(), m.getMarkerName(), Role.MARKER, m.getSubject(), paper.getExamNumber(), paper.getStudentName(), MarkLogOperType.LEVEl_DEVIATION.getId(), paper.getWorkId(), paper.getId(), m.getStage(), operResult, String.valueOf(d.getDeviation()), jsonObject.toJSONString(), work.getName());
                                markLogRepo.save(markLog);
                            } else {//更新日志
                                operResult = Optional.ofNullable(markLogDevi.getOperDataAfter()).orElse("-");
                                markLogDevi.setOperDataBefore(operResult);
                                markLogDevi.setOperDataAfter(String.valueOf(d.getDeviation()));
                                markLogDevi.setRemark(jsonObject.toJSONString());
                                markLogRepo.save(markLogDevi);
                            }
                        }
                    }
                }
                markTaskJobRepo.updateMarkTaskJobByVersion(markTaskJob.getId(), markTaskJob.getVersion());
                LOG.info("this:{},callback levelsLog异步回调结束了", this);
            }
        });
    }

    /**
     * 直接给试卷分档
     *
     * @param paper
     * @param level
     */
    @Transactional
    public Paper levelMarkPaper(Paper paper, String level, boolean isSample) {
        //2019.10.12 湖北美术统考去掉标准卷设置数量
//    	if(isSample){
//    		MarkSubject markSubject = markSubjectRepo.findOne(paper.getWorkId() + "-" + paper.getSubject().toString());
//    		long sampleCount = paperRepo.countByWorkIdAndSubjectAndIsSampleIsTrue(paper.getWorkId(), paper.getSubject());
//    		if(sampleCount >= markSubject.getSampleCount()){
//    			throw new RuntimeException("标准卷上限已达到");
//    		}
//    	}
        paper.determineLevel(level);
        paper.setMarkByLeader(true);
        paper.setSample(isSample);
        paperRepo.save(paper);
        List<MarkTask> markTasks = markTaskRepo.findByPaperIdAndStage(paper.getId(), MarkStage.LEVEL);
        for (MarkTask markTask : markTasks) {
//            markTask.setOriginLevel(markTask.getResult());
            markTask.setRejected(false);
            markTask.setResult(level);
        }
        markTaskRepo.save(markTasks);
        levelsLog(markTasks, paper);
        return paper;
    }

    /**
     * 打回重评
     * 更新试卷状态
     * 更新评卷任务状态为打回
     * 如果评卷任务的档位和建议档位一致，不打回该任务
     *
     * @param paper
     * @param redoLevel 建议重评档位
     * @param ranges
     */
    @Transactional
    public Paper reject(Paper paper, String redoLevel, String ranges) {
        paper.reject(redoLevel);
        //科组长打回标记
        paper.setRejectedByLeader(true);
        //打回后撤销标准卷
        paper.setSample(false);
        List<MarkTask> markTasks = markTaskRepo.findByPaperIdAndStage(paper.getId(), MarkStage.LEVEL);
        List<MarkLog> markLogList = new ArrayList<>();
        for (MarkTask markTask : markTasks) {
            /*if (markTask.getResult() != null && !markTask.getResult().toString().equals(redoLevel)) {
                markTask.setRejected(true);
                markTask.setOriginLevel(markTask.getResult());
                markTask.setResult(null);
            }*/
            String[] range = ranges.split(",");
            if (range.length > 0) {
                //加入手动打回日志
                String operResult = "-";
                for (String r : range) {
                    if (Objects.equals(String.valueOf(markTask.getMarkerId()), r)) {
                        markTask.setRejected(true);
                        markTask.setOriginLevel(markTask.getResult());
                        markTask.setResult(null);
                        MarkLog markLogPrev = markLogRepo.findTopByCreateUserIdAndOperTypeAndSubjectAndExamNumberOrderByCreateTimeDesc(markTask.getMarkerId(), MarkLogOperType.HANDLE_LEVEl.getId(), markTask.getSubject().toString(), paper.getExamNumber());
                        if (Objects.nonNull(markLogPrev)) {
                            operResult = Optional.ofNullable(markLogPrev.getOperDataAfter()).orElse("-");
                        }
                        Work work = workRepo.findOne(paper.getWorkId());
                        MarkLog markLog = new MarkLog(markTask.getMarkerId(), markTask.getMarkerName(), Role.MARKER, markTask.getSubject(), paper.getExamNumber(), paper.getStudentName(), MarkLogOperType.HANDLE_LEVEl.getId(), paper.getWorkId(), paper.getId(), markTask.getStage(), operResult, markTask.getOriginLevel(), MarkLogOperType.HANDLE_LEVEl.getName(), work.getName());
                        markLogList.add(markLog);
                    }
                }
            }
        }
        markTaskRepo.save(markTasks);
        paperRepo.save(paper);
        markLogRepo.save(markLogList);
        return paper;
    }

    /**
     * 打分。取出组内所有评分，如果都给了分，取平均分更新试卷成绩
     *
     * @param markTask
     * @param score
     */
    @Transactional
    public MarkTask scoring(MarkTask markTask, int score) {
        if (markTask.getStage() != MarkStage.SCORE) {
            throw new RuntimeException("该任务不是打分任务");
        }
        markTask.setResult(String.valueOf(score));
        markTask.setUpdatedOn(new Date());
        MarkUser maker = markUserRepo.findOne(markTask.getMarkerId());
        markTask.setMarkerName(maker.getName());
        markTaskRepo.save(markTask);
        Paper paper = markTask.getPaper();
        List<MarkTask> markTasks = markTaskRepo.findByPaperIdAndStage(paper.getId(), markTask.getStage());
        long leftCount = markTasks.stream().filter(i -> i.getResult() == null).count();
        if (leftCount == 0) {
            OptionalDouble finalScore = markTasks.stream().map(m -> m.getResult())
                    .mapToInt(Integer::valueOf).average();
            //湖北省，平均分非零进一
            Integer roundUp = Optional.ofNullable(ParamCache.paramMap.get(markTask.getWorkId()).getRoundUp()).orElse(1);
            double fs = roundUp.intValue() == 1 ? Math.ceil(finalScore.orElse(0)) : Math.round(finalScore.orElse(0));
            paper.setScore(fs);
            //改档打分完成，更新状态为false
            paper.setShiftScore(false);
            paperRepo.save(paper);
        }
        return markTask;
    }

    /**
     * 跳过该任务，最后处理
     *
     * @param markTaskId
     */
    @Transactional
    public MarkTask skip(Long markTaskId) {
        try {
            MarkTask m = markTaskRepo.findOne(markTaskId);
            Integer randomSeq = markTaskRepo.findRandomSeqByWorkIdAndSubjectAndMarkerIdAndStageAndQuestionId(m.getWorkId(), m.getSubject(), m.getMarkerId(), m.getStage(), m.getQuestionId());
            if (randomSeq == null) {
                randomSeq = 0;
            }
            m.setRandomSeq(randomSeq + 1);
            markTaskRepo.save(m);
            return m;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("该任务可能被占用，跳过失败");
        }

    }
}
