package cn.com.qmth.stmms.biz.mark.service.Impl;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import cn.com.qmth.stmms.biz.campus.model.Campus;
import cn.com.qmth.stmms.biz.campus.service.CampusService;
import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
import cn.com.qmth.stmms.biz.exam.model.Marker;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.utils.FormalTaskUtil;
import cn.com.qmth.stmms.biz.utils.TrialTaskUtil;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.MarkStatus;

/**
 * 与评卷相关的所有定时任务
 * 
 * @author luoshi
 *
 */
@Component
public class MarkCronService {

    protected static final Logger log = LoggerFactory.getLogger(MarkCronService.class);

    @Autowired
    private CampusService campusService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private MarkService markService;

    @Autowired
    private LockService lockService;

    @Autowired
    private MarkerService markerService;

    @Value("${mark.cleanTimeoutMinute}")
    private long timeoutMinute;

    /**
     * 自动释放可清除的锁
     */
    @Scheduled(cron = "${mark.cleanLockSchedule}")
    public void cronCleanLock() {
        try {
            lockService.clear();
        } catch (Exception e) {
            log.error("CronCleanTask error", e);
        }
    }

    /**
     * 自动释放超时未处理的评卷任务
     */
    @Scheduled(cron = "${mark.cleanTaskSchedule}")
    public void cronCleanTask() {
        try {
            FormalTaskUtil.clearTimeoutTask(timeoutMinute);
            TrialTaskUtil.clearTimeoutTask(timeoutMinute);
        } catch (Exception e) {
            log.error("CronCleanTask error", e);
        }
    }

    /**
     * 定时生成评卷任务
     */
    @Scheduled(fixedDelay = 60 * 1000, initialDelay = 60 * 1000)
    public void buildLibrary() {
        log.info("start auto-create library");
        try {
            Map<String, Campus> campusMap = new HashMap<String, Campus>();
            List<Integer> examIds = groupService.findExamIdByStatus(MarkStatus.TRIAL, MarkStatus.FORMAL);
            for (Integer examId : examIds) {
                buildLibraryByExam(examId, campusMap);
            }
        } catch (Exception e) {
            log.error("auto-create library error", e);
        } finally {
            log.info("finish auto-create library");
        }
    }

    /**
     * 定时更新评卷员质量监控指标
     */
    @Scheduled(fixedDelay = 60 * 1000, initialDelay = 60 * 1000)
    public void updateMarkerQuality() {
        log.debug("start auto-update marker");
        try {
            List<Integer> examIds = groupService.findExamIdByStatus(MarkStatus.FORMAL);
            for (Integer examId : examIds) {
                updateMarkerQualityByExam(examId);
            }
        } catch (Exception e) {
            log.error("auto-update marker error", e);
        } finally {
            log.debug("finish auto-update marker");
        }
    }

    private void updateMarkerQualityByExam(Integer examId) {
        // 获取主观题总分大于0的科目
        List<ExamSubject> subjects = subjectService.list(examId, 0);
        for (ExamSubject subject : subjects) {
            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subject.getCode(),
                    MarkStatus.FORMAL);
            for (MarkGroup markGroup : groups) {
                List<Marker> markers = markerService.findByExamAndSubjectAndGroup(examId, subject.getCode(),
                        markGroup.getNumber());
                for (Marker marker : markers) {
                    try {
                        markService.updateQuality(marker);
                    } catch (Exception e) {
                        log.error("update marker quality error for markerId=" + marker.getId(), e);
                    }
                }
            }
        }
    }

    private void buildLibraryByExam(Integer examId, Map<String, Campus> campusMap) {
        // 获取主观题总分大于0的科目
        List<ExamSubject> subjects = subjectService.listSubjectiveScore(examId, 0d);
        for (ExamSubject subject : subjects) {
            // 清除缺考考生和违纪考生
            List<ExamStudent> list = studentService.findAbsentOrBreachLibraryStudent(subject.getExamId(),
                    subject.getCode());
            if (list != null) {
                for (ExamStudent student : list) {
                    try {
                        lockService.waitlock(LockType.STUDENT, student.getId());
                        markService.deleteByStudent(student);
                    } catch (Exception e) {
                        log.error("delete student library error", e);
                    } finally {
                        lockService.unlock(LockType.STUDENT, student.getId());
                    }
                }
            }
            // 处理正常考生
            // 生成正评任务
            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
                    MarkStatus.FORMAL);
            for (MarkGroup group : groups) {
                buildFormalLibrary(subject, campusMap, group);
            }
            // 生成试评任务
            groups = groupService.findByExamAndSubjectAndStatus(subject.getExamId(), subject.getCode(),
                    MarkStatus.TRIAL);
            for (MarkGroup group : groups) {
                buildTrialLibrary(campusMap, group);
            }
        }
    }

    private void buildFormalLibrary(ExamSubject subject, Map<String, Campus> campusMap, MarkGroup group) {
        Date lastBuildTime = group.getBuildTime();
        ExamStudent student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(),
                group.getNumber(), lastBuildTime);
        while (student != null) {
            // 补充学习中心集合
            Campus campus = getCampus(student, campusMap);
            if (campus == null) {
                log.error("campus unexist for student id=" + student.getId());
                continue;
            }
            // 尝试构造评卷任务
            try {
                lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                lockService.watch(LockType.STUDENT, student.getId());
                // 上锁后重新验证状态
                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                        MarkStatus.FORMAL)) {
                    return;
                }
                if (studentService.validateStatus(student.getId())) {
                    markService.buildFormalLibrary(student, campus, group, subject);
                    markService.updateLibraryCount(group);
                    markService.updateMarkedCount(group);
                    lastBuildTime = student.getUploadTime();
                }
            } catch (Exception e) {
                log.error("build formal library error for studentId=" + student.getId() + ", groupNumber="
                        + group.getNumber(), e);
            } finally {
                lockService.unwatch(LockType.STUDENT, student.getId());
                lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            }
            // 取下一个考生
            student = studentService.findUnLibraryStudent(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                    lastBuildTime);
        }
    }

    private void buildTrialLibrary(Map<String, Campus> campusMap, MarkGroup group) {
        while (group.getTrialCount() > group.getLibraryCount()) {
            // 随机取一个未生成试评任务的考生
            ExamStudent student = studentService.randomUnTrialStudent(group.getExamId(), group.getSubjectCode(),
                    group.getNumber());
            if (student == null) {
                return;
            }
            // 补充学习中心集合
            Campus campus = getCampus(student, campusMap);
            if (campus == null) {
                log.error("campus unexist for student id=" + student.getId());
                continue;
            }
            // 尝试构造试评任务
            try {
                lockService.watch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
                lockService.watch(LockType.STUDENT, student.getId());
                // 上锁后重新验证状态
                if (!groupService.validateStatus(group.getExamId(), group.getSubjectCode(), group.getNumber(),
                        MarkStatus.TRIAL)) {
                    return;
                }
                if (studentService.validateStatus(student.getId())) {
                    markService.buildTrialLibrary(student, campus, group);
                    markService.updateLibraryCount(group);
                    markService.updateMarkedCount(group);
                }
            } catch (Exception e) {
                log.error("build trial library error for studentId=" + student.getId() + ", groupNumber="
                        + group.getNumber(), e);
            } finally {
                lockService.unwatch(LockType.STUDENT, student.getId());
                lockService.unwatch(LockType.GROUP, group.getExamId(), group.getSubjectCode(), group.getNumber());
            }
        }
    }

    private Campus getCampus(ExamStudent student, Map<String, Campus> campusMap) {
        // 补充学习中心集合
        Campus campus = campusMap.get(student.getSchoolId() + "_" + student.getCampusName());
        if (campus == null) {
            campus = campusService.findBySchoolAndName(student.getSchoolId(), student.getCampusName());
            if (campus != null) {
                campusMap.put(student.getSchoolId() + "_" + student.getCampusName(), campus);
            }
        }
        return campus;
    }
}
