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

import cn.com.qmth.stmms.ms.commons.utils.RandomUtil;
import cn.com.qmth.stmms.ms.commons.utils.SqlUtil;
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.MarkerGroup;
import cn.com.qmth.stmms.ms.core.repository.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 评卷任务分配
 * Created by zhengmin on 2016/9/23.
 */
@Service
public class AssignTaskService {

    @Autowired
    private MarkTaskRepo markTaskRepo;

    @Autowired
    private PaperRepo paperRepo;

    @Autowired
    private LevelRepo levelRepo;

    @Autowired
    private MarkSubjectRepo markSubjectRepo;

    @Autowired
    private MarkerGroupRepo markerGroupRepo;

    @Autowired
    private TaskPublishSettingRepo taskPublishSettingRepo;

    @Value("${sys.config.random-bundle-size}")
    private int randomBundleSize;

    @Autowired
    RandomUtil randomUtil;
    
    @Autowired
    SqlUtil sqlUtil;

    /**
     * 单评任务模式，每个科目评卷员都需要对该科目每份试卷进行评卷
     * 比如，色彩有1000份试卷，那么每个评卷员都需要评1000份
     *
     * @param papers
     * @param markers
     */
    @Transactional
    public void assignForAll(List<Paper> papers, Iterable<MarkUser> markers, MarkSubject markSubject) throws Exception {
        List<MarkTask> markTasks = new ArrayList<>();
        int paperCount = papers.size();
        int randomBundleCount = paperCount / randomBundleSize;
        if (paperCount % randomBundleSize != 0) {
            randomBundleCount += 1;
        }
        List<MarkTask> markTaskList = markTaskRepo.findByWorkId(markSubject.getWorkId());
        Map<Long, MarkTask> randomMap = markTaskList.stream().collect(Collectors.toMap(MarkTask::getRandomSeqNew, Function.identity(), (dto1, dto2) -> dto1));
        for (int i = 0; i < randomBundleCount; i++) {
            int fromIndex = i * randomBundleSize;
            int endIndex = i * randomBundleSize + randomBundleSize;
            if (endIndex > paperCount) {
                endIndex = paperCount;
            }
            List<Paper> bundleList = papers.subList(fromIndex, endIndex);

            for (MarkUser marker : markers) {
                List<Paper> markerPaperList = Arrays.asList(new Paper[bundleList.size()]);

                Collections.copy(markerPaperList, bundleList);
                Collections.shuffle(markerPaperList);//乱序

                for (int j = 0; j < markerPaperList.size(); j++) {
                    Paper paper = markerPaperList.get(j);
                    Long random = getRandom(marker.getId(), paper.getId(), marker.getWorkId(), paper.getExamNumber(), randomMap);
                    MarkTask markTask = new MarkTask(marker, paper, markSubject.getStage(), random, markSubject.getTest());
                    markTask.setRandomSeq(i * randomBundleSize + j + 1);
                    markTasks.add(markTask);
                }
            }

        }

        markTaskRepo.save(markTasks);

//        Iterator<Paper> iterator = papers.iterator();
//        while (iterator.hasNext()) {
//            Paper paper = iterator.next();
//            for (MarkUser marker : markers) {
//                MarkTask markTask = new MarkTask(marker, paper, markSubject.getStage());
//                markTaskRepo.save(markTask);
//                //taskExecutor.execute(papers,marker,stage);
//            }
//        }
    }

    /**
     * 分组任务模式，将该科目的评卷员分成N组，将该科目试卷平均分给这些组，每组内的评卷员每人将这些试卷打分
     * 比如：6个评卷员分2组，有1000份试卷，每组评500份，每个评卷员评500份
     *  @param papers
     * @param markerGroups
     * @param taskList
     */
    @Transactional
    public void assignForGrouping(List<Paper> papers, List<MarkerGroup> markerGroups, MarkSubject markSubject, List<TaskPublishSetting> taskList) throws Exception {
        markerGroups = markerGroups.stream().filter(m -> m.getMarkers().size() > 0).collect(Collectors.toList());
        int sum = markerGroups.stream().mapToInt(m -> m.getMarkers().size()).sum();
        int groupSize = markerGroups.size();
        int idx = 0;
        long currentTime = System.currentTimeMillis();
        Iterator<Paper> iterator = papers.iterator();
        List<MarkTask> markTaskList = new ArrayList<>(papers.size() * sum);
        List<Paper> paperList = new ArrayList<>(papers.size());
        List<MarkTask> markTasks = markTaskRepo.findByWorkId(markSubject.getWorkId());
        Map<Long, MarkTask> randomMap = markTasks.stream().collect(Collectors.toMap(MarkTask::getRandomSeqNew, Function.identity(), (dto1, dto2) -> dto1));
        //定序序号记录
        Map map = new HashMap();
        while (iterator.hasNext()) {
            if (idx >= groupSize) {
                idx = 0;
            }
            Paper paper = iterator.next();
            String serialNumber = null;
            Integer displayNumber = null;
            if (Objects.equals(MarkStage.SCORE, markSubject.getStage())) {
                //是否定序
                int seq = 0;
                long sortRule = taskList.stream().filter(m -> Objects.equals(m.getCode(), paper.getLevel()) && m.getSortRule() == 1).count();
                if (sortRule != 0) {
                    if (map.containsKey(paper.getLevel() + idx)) {
                        seq = (int) map.get(paper.getLevel() + idx);
                    }
                    seq = ++seq;
                    serialNumber = paper.getLevel() + seq;
                    //是否显示序号
                    long display = taskList.stream().filter(m -> Objects.equals(m.getCode(), paper.getLevel()) && m.getDisplayNumber() == 1).count();
                    displayNumber = (int) display;
                    //设置新值
                    map.put(paper.getLevel() + idx, seq);
                }
            }
            for (MarkUser marker : markerGroups.get(idx).getMarkers()) {
                Long random = getRandom(marker.getId(), paper.getId(), marker.getWorkId(), paper.getExamNumber(), randomMap);
                MarkTask markTask = null;
                if (Objects.equals(MarkStage.SCORE, markSubject.getStage())) {
                    markTask = new MarkTask(marker, paper, markSubject.getStage(), random, markSubject.getTest());
                    markTask.setBatchNo(currentTime);
                    markTask.setSerialNumber(serialNumber);
                    markTask.setDisplayNumber(displayNumber);
                } else {
                    if(paper.isSample() && paper.getLevel() != null){
                        Level level = levelRepo.findByWorkIdAndCode(markSubject.getWorkId(), paper.getLevel());
                        markTask = new MarkTask(marker, paper, level, markSubject.getStage(), random, markSubject.getTest());
                    } else {
                        markTask = new MarkTask(marker, paper, markSubject.getStage(), random, markSubject.getTest());
                    }
                }
                markTaskList.add(markTask);
                randomMap.put(random, markTask);
//                markTaskRepo.save(markTask);
            }
            if (markSubject.getTest() != 1) {
                if (MarkStage.LEVEL.equals(markSubject.getStage())) {
                    //将paper置为激活
                    paper.setActive(true);
                    paper.setBatchNo(currentTime);
//                    paperRepo.save(paper);
                    paperList.add(paper);
                    //更新subject表formal为正试评卷
//                    markSubject.setFormal(true);
//                    markSubjectRepo.saveAndFlush(markSubject);
                }
                if (MarkStage.SCORE.equals(markSubject.getStage())) {
                    paper.setScoreBatchNo(currentTime);
                    paperList.add(paper);
                }
            }
            idx++;
        }
        markTaskRepo.save(markTaskList);
        paperRepo.save(paperList);
        if (markSubject.getTest() != 1) {
            //更新subject表formal为正试评卷
            markSubject.setFormal(true);
            markSubjectRepo.saveAndFlush(markSubject);
        }
        //只有全部任务投放完成，才能删除分组
        int count = paperRepo.countByWorkIdAndSubjectAndIsMissingFalseAndActiveFalseAndBatchNoIsNullAndTest(markSubject.getWorkId(), markSubject.getSubject(), 0);
        //进入分档阶段后，清空当前分组，以便打分阶段前重新分组
        if (MarkStage.LEVEL.equals(markSubject.getStage()) && count == 0) {
            for (MarkerGroup markerGroup : markerGroups) {
                markerGroup.setMarkers(null);
            }
            markerGroupRepo.delete(markerGroups);
        }
//        if (MarkStage.SCORE.equals(markSubject.getStage()) && count == 0) {
//            //初始化打分任务数据
//            initTaskPublishData(markSubject);
//
//        }
    }

    @Transactional
    public void assignForGroupingScore(List<MarkerGroup> markerGroups, MarkSubject markSubject) {
        markerGroups = markerGroups.stream().filter(m -> m.getMarkers().size() > 0).collect(Collectors.toList());
        int sum = markerGroups.stream().mapToInt(m -> m.getMarkers().size()).sum();
        List<MarkTask> markTasks = markTaskRepo.findByWorkId(markSubject.getWorkId());
        if (markSubject.getTest() != 1) {
            //更新subject表formal为正试评卷
            markSubject.setFormal(true);
            markSubjectRepo.saveAndFlush(markSubject);
        }
        //只有全部任务投放完成，才能删除分组
        int count = paperRepo.countByWorkIdAndSubjectAndIsMissingFalseAndActiveFalseAndBatchNoIsNullAndTest(markSubject.getWorkId(), markSubject.getSubject(), 0);
        //进入分档阶段后，清空当前分组，以便打分阶段前重新分组
        if (MarkStage.LEVEL.equals(markSubject.getStage()) && count == 0) {
            for (MarkerGroup markerGroup : markerGroups) {
                markerGroup.setMarkers(null);
            }
            markerGroupRepo.delete(markerGroups);
        }
        if (MarkStage.SCORE.equals(markSubject.getStage()) && count == 0) {
            //初始化打分任务数据
            initTaskPublishData(markSubject);

        }
    }

    private void initTaskPublishData(MarkSubject markSubject) {
        StringBuffer sql = new StringBuffer();
        sql.append("  SELECT                                                                    ");
        sql.append("          p.work_id workId,                                                  ");
        sql.append("          p.question_id questionId,                                          ");
        sql.append("          p.subject,                                                         ");
        sql.append("          p.level,                                                           ");
        sql.append("              COUNT(1) AS totalCount                                         ");
//        sql.append("              SUM(IF(p.score IS NOT NULL, 1, 0)) successCount,               ");
//        sql.append("              SUM(IF(p.score IS NULL, 1, 0)) waitCount                        ");
        sql.append("      FROM                                                                   ");
        sql.append("          paper p                                                            ");
        sql.append("      WHERE                                                                  ");
        sql.append("          p.work_id = '"+markSubject.getWorkId()+"' AND p.subject = '"+markSubject.getSubject().name()+"'                                    ");
        sql.append("              AND p.is_test = 0                                              ");
        sql.append("              AND p.level IS NOT NULL                                        ");
        sql.append("              AND p.is_missing = FALSE                                       ");
        sql.append("              AND p.is_active = TRUE                                         ");
        sql.append("      GROUP BY p.work_id , p.question_id, p.subject , p.level                ");
        sql.append("  ORDER BY p.level                                                            ");
        List list =  sqlUtil.execSqlForMap(sql.toString());
        List<TaskPublishSetting> taskPublishSettings = new ArrayList<>(list.size());
        for (Object o : list) {
            Map map = (Map) o;
            TaskPublishSetting taskPublishSetting = new TaskPublishSetting();
            taskPublishSetting.setWorkId(Long.valueOf(map.get("workId").toString()));
            taskPublishSetting.setQuestionId(Long.valueOf(map.get("questionId").toString()));
            taskPublishSetting.setSubject((String) map.get("subject"));
            taskPublishSetting.setCode((String) map.get("level"));
            taskPublishSetting.setTotalCount(Integer.valueOf(map.get("totalCount").toString()));
            taskPublishSetting.setSuccessCount(0);
            taskPublishSetting.setWaitCount(Integer.valueOf(map.get("totalCount").toString()));
            taskPublishSetting.setSortRule(0);
            taskPublishSetting.setPublishCount(0);
            taskPublishSettings.add(taskPublishSetting);
        }
        taskPublishSettingRepo.save(taskPublishSettings);
    }

    /**
     * 获取随机号
     *
     * @param markerId
     * @param paperId
     * @param workId
     * @param examNumber
     * @param randomMap
     * @return
     * @throws Exception
     */
    public Long getRandom(Long markerId, Long paperId, Long workId, String examNumber, Map<Long, MarkTask> randomMap) throws Exception {
        int count = 0;
        Long random = 0L;
        while (true) {
            random = randomUtil.getRandomMap().get(workId).get(new Random().nextInt(randomUtil.getRandomMap().get(workId).size()));
            if (Objects.isNull(randomMap) || randomMap.size() == 0) {
                break;
            }
            if (Objects.isNull(randomMap.get(random))) {
                break;
            } else {
                count++;
            }
            if (count > 1000) {
//                throw new Exception("重复几率较高，建议重新生成随机号");
                randomUtil.getRandom(workId, true);
                getRandom(markerId, paperId, workId, examNumber, randomMap);
            }
        }
        return random;
    }
}
