package cn.com.qmth.stmms.admin.exam;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import net.sf.json.JSONObject;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import cn.com.qmth.stmms.admin.dto.RejectResult;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.ExamQuestion;
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.query.MarkerSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamQuestionService;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.file.enums.FormatType;
import cn.com.qmth.stmms.biz.file.service.FileService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.model.MarkLibrary;
import cn.com.qmth.stmms.biz.mark.model.Task;
import cn.com.qmth.stmms.biz.mark.query.MarkLibrarySearchQuery;
import cn.com.qmth.stmms.biz.mark.service.MarkLibraryService;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.mark.service.TaskService;
import cn.com.qmth.stmms.biz.user.model.User;
import cn.com.qmth.stmms.biz.user.service.UserService;
import cn.com.qmth.stmms.common.annotation.Logging;
import cn.com.qmth.stmms.common.annotation.RoleRequire;
import cn.com.qmth.stmms.common.domain.WebUser;
import cn.com.qmth.stmms.common.enums.LibraryStatus;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.LogType;
import cn.com.qmth.stmms.common.enums.MarkStatus;
import cn.com.qmth.stmms.common.enums.Role;
import cn.com.qmth.stmms.common.utils.RequestUtils;

@Controller("libraryController")
@RequestMapping("/admin/exam/library")
public class LibraryController extends BaseExamController {

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

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private MarkLibraryService libraryService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private MarkerService markerService;

    @Autowired
    private MarkService markService;

    @Autowired
    private LockService lockService;

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private ExamService examService;

    @Autowired
    private FileService fileService;

    @Autowired
    private UserService userService;

    @Autowired
    private TaskService taskService;

    @Logging(menu = "评卷任务查询", type = LogType.QUERY)
    @RequestMapping
    public String list(Model model, HttpServletRequest request, MarkLibrarySearchQuery query,
            @RequestParam(required = false) LibraryStatus status) {
        int examId = getSessionExamId(request);
        WebUser wu = RequestUtils.getWebUser(request);
        List<ExamSubject> subjectList = getExamSubject(examId, wu);
        if (subjectList.isEmpty()) {
            return "redirect:/admin/exam/mark";
        }
        query.setExamId(examId);
        if (status != null) {
            query.addStatus(status);
        }
        if (status != LibraryStatus.REJECTED) {
            query.setRejectReason(null);
        }
        if (StringUtils.isEmpty(query.getSubjectCode()) && !subjectList.isEmpty()) {
            query.setSubjectCode(subjectList.get(0).getCode());
        }
        List<MarkGroup> groupList = groupService.findByExamAndSubjectAndStatus(examId, query.getSubjectCode(),
                MarkStatus.FORMAL);
        if (!groupList.isEmpty()) {
            query = libraryService.findByQuery(query);
            for (MarkLibrary library : query.getResult()) {
                if (library.getMarkerId() != null) {
                    User marker = userService.findByMarkerId(library.getMarkerId());
                    library.setMarkerLoginName(marker.getLoginName() + "/" + marker.getName());
                    if (library.getHeaderId() != null) {
                        User header = userService.findById(library.getHeaderId());
                        library.setHeaderLoginName(header.getLoginName() + "/" + header.getName());
                    }
                }
            }
            for (MarkGroup group : groupList) {
                group.setQuestionList(questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                        group.getSubjectCode(), false, group.getNumber()));
            }
        }
        long inspectedCount = 0;
        if (LibraryStatus.MARKED.equals(status) || status == null) {
            query.addStatus(LibraryStatus.MARKED);
            inspectedCount = libraryService.countByQuery(query);
        }
        model.addAttribute("inspectedCount", inspectedCount);
        model.addAttribute("query", query);
        model.addAttribute("subjectList", getExamSubject(examId, wu));
        model.addAttribute("groupList", groupList);
        model.addAttribute("statusList", LibraryStatus.getOptionList());
        model.addAttribute("status", status);
        List<Marker> markerList = new ArrayList<Marker>();
        if (query.getGroupNumber() != null) {
            MarkerSearchQuery mQuery = new MarkerSearchQuery();
            mQuery.setExamId(examId);
            mQuery.setSubjectCode(query.getSubjectCode());
            mQuery.setGroupNumber(query.getGroupNumber());
            mQuery.setPageSize(Integer.MAX_VALUE);
            markerList = markerService.findByQuery(mQuery).getResult();
            for (Marker marker : markerList) {
                marker.setLoginName(userService.findById(marker.getUserId()).getLoginName());
            }
        }
        model.addAttribute("markerList", markerList);
        Exam exam = examService.findById(examId);
        model.addAttribute("examType", exam.getType());
        return "modules/exam/libraryList";
    }

    @Logging(menu = "打回或重置评卷任务", type = LogType.UPDATE)
    @RequestMapping(value = "/reject", method = RequestMethod.POST)
    @ResponseBody
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public JSONObject reject(HttpServletRequest request, @RequestParam Integer id,
            @RequestParam(required = false, defaultValue = "false") boolean isRest,
            @RequestParam(required = false) String reason) {
        JSONObject obj = new JSONObject();
        MarkLibrary library = libraryService.findById(id);
        WebUser wu = RequestUtils.getWebUser(request);
        if (library != null) {
            if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
                try {
                    lockService.watch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
                            library.getGroupNumber());
                    lockService.waitlock(LockType.STUDENT, library.getStudentId());
                    if ((library.getStatus().equals(LibraryStatus.MARKED)
                            || library.getStatus().equals(LibraryStatus.PROBLEM) || library.getStatus().equals(
                            LibraryStatus.INSPECTED))
                            && markService.rejectLibrary(library, wu.getId(), reason, isRest)) {
                        obj.accumulate("success", true);
                    } else {
                        obj.accumulate("success", false);
                        obj.accumulate("message", "无法打回该评卷任务");
                    }
                } catch (Exception e) {
                    obj.accumulate("success", false);
                    obj.accumulate("message", "打回评卷任务失败");
                    log.error("back library error", e);
                } finally {
                    lockService.unlock(LockType.STUDENT, library.getStudentId());
                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
                            library.getGroupNumber());
                    lockService.unwatch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
                }
            } else {
                obj.accumulate("success", false);
                obj.accumulate("message", "没有操作该评卷任务的权限");
            }
        } else {
            obj.accumulate("success", false);
            obj.accumulate("message", "该评卷任务不存在");
        }
        return obj;
    }

    @RequestMapping(value = "/getJson", method = RequestMethod.GET)
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.SCHOOL_VIEWER, Role.COLLEGE_ADMIN })
    public String getJson(Model model, HttpServletRequest request, @RequestParam Integer studentId,
            @RequestParam(required = false) Integer groupNumber) {
        int examId = getSessionExamId(request);
        ExamStudent student = studentService.findById(studentId);
        List<ExamQuestion> questions = questionService.findByExamAndSubjectAndObjective(examId,
                student.getSubjectCode(), false);
        if (groupNumber != null) {
            questions = questionService.findByExamAndSubjectAndObjectiveAndGroupNumber(examId,
                    student.getSubjectCode(), false, groupNumber);
        }
        List<String> strings = new ArrayList<String>();
        for (ExamQuestion examQuestion : questions) {
            strings.add(examQuestion.getQuestionNumber());
        }
        String questionNumbers = String.join(",", strings);
        model.addAttribute("questionNumbers", questionNumbers);
        model.addAttribute("fileServer", fileService.getFileServer());
        model.addAttribute("answerUrl", fileService.getJsonUri(student.getExamId(), student.getSecretNumber()));
        model.addAttribute("paperUrl",
                fileService.getPaperUri(student.getExamId(), student.getSubjectCode(), FormatType.JSON));
        return "modules/exam/jsonView";
    }

    @Logging(menu = "开始任务复核", type = LogType.QUERY)
    @RequestMapping(value = "/getTask", method = RequestMethod.POST)
    @ResponseBody
    public Task getTask(HttpServletRequest request, RedirectAttributes redirectAttributes, MarkLibrarySearchQuery query) {
        int examId = getSessionExamId(request);
        MarkGroup group = groupService.findOne(examId, query.getSubjectCode(), query.getGroupNumber());
        if (group == null) {
            return null;
        } else if (group.getStatus() == MarkStatus.FINISH) {
            return null;
        } else if (group.getStatus() == MarkStatus.TRIAL) {
            return null;
        }
        Task task = null;
        WebUser wu = RequestUtils.getWebUser(request);
        int retry = 1;
        while (task == null) {
            query.setExamId(examId);
            query.addStatus(LibraryStatus.MARKED);
            query.setPageNumber(retry);
            query.setPageSize(20);
            query = libraryService.findByQuery(query);
            if (query.getResult().isEmpty()) {
                break;
            }
            for (MarkLibrary library : query.getResult()) {
                if (libraryService.applyLibrary(library, wu.getId())) {
                    task = taskService.build(library);
                    break;
                }
            }
            if (task == null) {
                retry++;
            }
        }
        return task;
    }

    @Logging(menu = "考生评卷复核", type = LogType.UPDATE)
    @RequestMapping(value = "/inspected/save", method = RequestMethod.POST)
    @ResponseBody
    public Object save(HttpServletRequest request, @RequestParam Integer libraryId) {
        WebUser wu = RequestUtils.getWebUser(request);
        MarkLibrary library = libraryService.findById(libraryId);
        JSONObject obj = new JSONObject();
        try {
            if (libraryId != null && library.getStatus().equals(LibraryStatus.MARKED)
                    && libraryService.hasApplied(library, wu.getId())) {
                library.setHeaderId(wu.getUser().getId());
                library.setHeaderTime(new Date());
                library.setStatus(LibraryStatus.INSPECTED);
                libraryService.save(library);
                libraryService.releaseByLibrary(library);
                obj.accumulate("success", true);
            } else {
                obj.accumulate("success", false);
                obj.accumulate("message", "无法复核，请刷新页面");
            }
        } catch (Exception e) {
            obj.accumulate("success", false);
            obj.accumulate("message", "无法复核，请刷新页面!");
            log.error("inspected library save error", e);
        }
        return obj;
    }

    @RequestMapping(value = "/clear", method = RequestMethod.POST)
    @ResponseBody
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.COLLEGE_ADMIN })
    public Object clear(HttpServletRequest request, @RequestParam String subjectCode,
            @RequestParam Integer groupNumber, @RequestParam(required = false) Integer libraryId) {
        WebUser wu = RequestUtils.getWebUser(request);
        JSONObject obj = new JSONObject();
        if (libraryId != null) {
            releaseLibrary(libraryId);
            obj.accumulate("success", true);
        } else {
            int examId = getSessionExamId(request);
            releaseUser(examId, subjectCode, groupNumber, wu.getUser().getId());
            obj.accumulate("success", true);
        }
        return obj;
    }

    @RequestMapping(value = "/getStatus", method = RequestMethod.POST)
    @ResponseBody
    public JSONObject status(HttpServletRequest request, MarkLibrarySearchQuery query) {
        JSONObject status = new JSONObject();
        int examId = getSessionExamId(request);
        query.setExamId(examId);
        query.addStatus(LibraryStatus.MARKED);
        long inspectedCount = libraryService.countByQuery(query);
        status.accumulate("totalCount", inspectedCount);
        status.accumulate("valid", true);
        return status;
    }

    private void releaseUser(Integer examId, String subjectCode, Integer groupNumber, Integer userId) {
        try {
            libraryService.releaseByUserId(examId, subjectCode, groupNumber, userId);
        } catch (Exception e) {
            log.error("release user error", e);
        }
    }

    private void releaseLibrary(Integer libraryId) {
        try {
            MarkLibrary library = libraryService.findById(libraryId);
            libraryService.releaseByLibrary(library);
        } catch (Exception e) {
            log.error("release library error", e);
        }
    }

    @Logging(menu = "打回", type = LogType.UPDATE)
    @RequestMapping(value = "/rejected", method = RequestMethod.POST)
    @ResponseBody
    @RoleRequire({ Role.SCHOOL_ADMIN, Role.SUBJECT_HEADER, Role.INSPECTOR, Role.COLLEGE_ADMIN })
    public JSONObject rejected(HttpServletRequest request, @RequestBody RejectResult rejectResult) {
        JSONObject obj = new JSONObject();
        WebUser wu = RequestUtils.getWebUser(request);
        MarkLibrary library = libraryService.findById(rejectResult.getLibraryId());
        if (library != null) {
            if (subjectCheck(library.getSubjectCode(), RequestUtils.getWebUser(request))) {
                try {
                    lockService.watch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
                    lockService.watch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
                            library.getGroupNumber());
                    lockService.waitlock(LockType.STUDENT, library.getStudentId());
                    if (((libraryService.hasApplied(library, wu.getId())
                            && (library.getStatus().equals(LibraryStatus.MARKED)) || library.getStatus().equals(
                            LibraryStatus.INSPECTED)) && markService.rejectLibrary(library,
                            rejectResult.getQuestionList(), wu.getId(), rejectResult.getReason()))) {
                        libraryService.releaseByLibrary(library);
                        obj.accumulate("success", true);
                    } else {
                        obj.accumulate("success", false);
                        obj.accumulate("message", "无法打回该评卷任务");
                    }
                } catch (Exception e) {
                    obj.accumulate("success", false);
                    obj.accumulate("message", "打回评卷任务失败");
                    log.error("back library error", e);
                } finally {
                    lockService.unlock(LockType.STUDENT, library.getStudentId());
                    lockService.unwatch(LockType.GROUP, library.getExamId(), library.getSubjectCode(),
                            library.getGroupNumber());
                    lockService.unwatch(LockType.EXAM_SUBJECT, library.getExamId(), library.getSubjectCode());
                }
            } else {
                obj.accumulate("success", false);
                obj.accumulate("message", "没有操作该评卷任务的权限");
            }
        } else {
            obj.accumulate("success", false);
            obj.accumulate("message", "该评卷任务不存在");
        }
        return obj;
    }

    @Logging(menu = "回看评卷复核任务", type = LogType.QUERY)
    @RequestMapping(value = "/getHistory", method = RequestMethod.POST)
    @ResponseBody
    public Object getHistory(HttpServletRequest request, @RequestParam String subjectCode,
            @RequestParam Integer groupNumber, @RequestParam int pageNumber, @RequestParam int pageSize)
            throws Exception {
        int examId = getSessionExamId(request);
        WebUser wu = RequestUtils.getWebUser(request);
        List<Task> list = new ArrayList<>();
        MarkLibrarySearchQuery query = new MarkLibrarySearchQuery();
        query.setExamId(examId);
        query.setSubjectCode(subjectCode);
        query.setGroupNumber(groupNumber);
        query.addStatus(LibraryStatus.INSPECTED);
        query.setHeaderId(wu.getId());
        query.setPageNumber(pageNumber);
        query.setPageSize(pageSize);
        query.orderByHeaderTimeDesc();
        query = libraryService.findByQuery(query);
        for (MarkLibrary library : query.getResult()) {
            Task task = taskService.build(library);
            task.setPrevious(true);
            list.add(task);
        }
        return list;
    }
}
