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

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import cn.com.qmth.stmms.admin.dto.ExamStudentDTO;
import cn.com.qmth.stmms.admin.dto.ScoreEditDTO;
import cn.com.qmth.stmms.admin.dto.ScoreExportDTO;
import cn.com.qmth.stmms.admin.thread.ScoreCalculateThread;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.ExamPackage;
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.query.ExamStudentSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamPackageService;
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.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.MarkGroupService;
import cn.com.qmth.stmms.biz.lock.LockService;
import cn.com.qmth.stmms.biz.mark.service.MarkService;
import cn.com.qmth.stmms.biz.report.service.ReportService;
import cn.com.qmth.stmms.biz.utils.ScoreItem;
import cn.com.qmth.stmms.common.auth.annotation.RoleRequire;
import cn.com.qmth.stmms.common.domain.WebUser;
import cn.com.qmth.stmms.common.enums.LockType;
import cn.com.qmth.stmms.common.enums.MarkStatus;
import cn.com.qmth.stmms.common.enums.Role;
import cn.com.qmth.stmms.common.utils.ExportExcel;
import cn.com.qmth.stmms.common.utils.PictureUrlBuilder;
import cn.com.qmth.stmms.common.utils.RequestUtils;
import net.sf.json.JSONObject;

@Controller
@RequestMapping("/admin/exam/score")
public class ScoreController extends BaseExamController {

    @Autowired
    private ExamSubjectService subjectService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private ExamQuestionService questionService;

    @Autowired
    private ExamPackageService packageService;

    @Autowired
    private MarkService markService;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private AsyncTaskExecutor taskExecutor;

    @Autowired
    private LockService lockService;

    @Autowired
    private ExamService examService;

    @Autowired
    private ReportService reportService;

    @Value("${sheet.image.server}")
    private String imageServer;

    @Value("${package.image.server}")
    private String packageServer;

    @Value("${card.server}")
    private String cardServer;

    @RequestMapping
    public ModelAndView list(HttpServletRequest request, ExamStudentSearchQuery query,
            @RequestParam(defaultValue = "0") Integer filter) {
        WebUser wu = RequestUtils.getWebUser(request);
        int examId = getSessionExamId(request);
        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
        ModelAndView view = new ModelAndView("modules/exam/scoreList");
        if (subjectCode != null) {
            query.setSubjectCode(subjectCode);
        }
        query.setExamId(examId);
        query.orderByExamNumber();
        if (filter == 1) {
            query.setObjectiveScore(0d);
            query.setSubjectiveScoreGt(0d);
        } else if (filter == 2) {
            query.setObjectiveScoreGt(0d);
            query.setSubjectiveScore(0d);
        }
        subjectFilter(query, wu);
        query = studentService.findByQuery(query);
        for (ExamStudent student : query.getResult()) {
            buildSheetUrl(student);
            buildPackageUrl(student);
            buildAnswerUrl(student);
        }
        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())
                ? enableExport(examId, query.getSubjectCode())
                : enableExport(examId);
        if (exportMessage != null) {
            view.addObject("exportMessage", exportMessage);
            view.addObject("enableExport", false);
        } else {
            view.addObject("enableExport", true);
        }
        view.addObject("query", query);
        view.addObject("filter", filter);
        view.addObject("subjectList", getExamSubject(examId, wu));
        view.addObject("running", lockService.isLocked(LockType.SCORE_CALCULATE, examId));
        view.addObject("imageServer", imageServer);
        view.addObject("packageServer", packageServer);
        view.addObject("cardServer", cardServer);
        return view;
    }

    @RequestMapping("/detail")
    public ModelAndView detail(HttpServletRequest request, @RequestParam String examNumber,
            @RequestParam String queryString) {
        int examId = getSessionExamId(request);
        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, examNumber);
        if (student != null) {
            buildSheetUrl(student);
            buildPackageUrl(student);
            buildAnswerUrl(student);
            ModelAndView view = new ModelAndView("modules/exam/scoreDetail");
            view.addObject("student", student);
            view.addObject("scoreList", buildScoreList(student));
            view.addObject("imageServer", imageServer);
            view.addObject("packageServer", packageServer);
            view.addObject("cardServer", cardServer);
            view.addObject("queryString", StringUtils.trimToEmpty(queryString));
            return view;
        } else {
            return new ModelAndView("redirect:/admin/exam/score");
        }
    }

    @RequestMapping("/edit")
    @ResponseBody
    @RoleRequire(Role.SCHOOL_ADMIN)
    public JSONObject edit(HttpServletRequest request, ScoreEditDTO dto) {
        JSONObject result = new JSONObject();
        int examId = getSessionExamId(request);
        ExamStudent student = studentService.findByExamIdAndExamNumber(examId, dto.getExamNumber());
        if (student != null) {
            double oScore = 0d;
            List<ScoreItem> oList = dto.getObjectiveScoreItems();
            for (ScoreItem item : oList) {
                oScore += item.getScore();
            }
            student.setObjectiveScore(oScore);
            student.setScoreList(oList, true);

            double sScore = 0d;
            List<ScoreItem> sList = dto.getSubjectiveScoreItems();
            for (ScoreItem item : sList) {
                sScore += item.getScore();
            }
            student.setSubjectiveScore(sScore);
            student.setScoreList(sList, false);

            studentService.save(student);
            result.accumulate("success", true);
        } else {
            result.accumulate("success", false);
            result.accumulate("message", "找不到对应的考生");
        }
        return result;
    }

    @RequestMapping("/calculate")
    @RoleRequire(Role.SCHOOL_ADMIN)
    public ModelAndView calculate(HttpServletRequest request) {
        int examId = getSessionExamId(request);
        if (!lockService.isLocked(LockType.SCORE_CALCULATE, examId)) {
            ScoreCalculateThread thread = new ScoreCalculateThread(examId, lockService, studentService, questionService,
                    markService, reportService, examService, subjectService, groupService);
            taskExecutor.submit(thread);
        }
        return new ModelAndView("redirect:/admin/exam/score");
    }

    @RequestMapping("/export")
    public String export(ExamStudentSearchQuery query, HttpServletRequest request, HttpServletResponse response,
            RedirectAttributes redirectAttributes) {
        WebUser wu = RequestUtils.getWebUser(request);
        int examId = getSessionExamId(request);
        String exportMessage = StringUtils.isNotBlank(query.getSubjectCode())
                ? enableExport(examId, query.getSubjectCode())
                : enableExport(examId);
        if (exportMessage == null && StringUtils.isNotBlank(query.getStudentCode())) {
            exportMessage = enableExport(examId, query.getStudentCode());
        }
        if (exportMessage != null) {
            addMessage(redirectAttributes, "评卷未结束不能导出成绩 " + exportMessage);
            return "redirect:/admin/exam/score";
        }
        String subjectCode = RequestUtils.getSession(request).getParameter("subjectCode");
        query.setExamId(examId);
        query.setPageNumber(1);
        query.setPageSize(Integer.MAX_VALUE);
        subjectFilter(query, wu);
        query = studentService.findByQuery(query);
        String fileName = "成绩单.xlsx";
        try {
            if (subjectCode != null) {
                fileName = subjectService.find(examId, subjectCode).getName() + "-机阅考试成绩表";
                List<ScoreExportDTO> list = new LinkedList<ScoreExportDTO>();
                for (ExamStudent student : query.getResult()) {
                    if (student.isBreach() || student.isAbsent()) {
                        student.setObjectiveScore(0d);
                        student.setSubjectiveScore(0d);
                    }
                    list.add(new ScoreExportDTO(student));
                }
                new ExportExcel(subjectCode + "-" + subjectService.find(examId, subjectCode).getName(),
                        ScoreExportDTO.class).setDataList(list).write(response, fileName + ".xlsx").dispose();
                return null;
            }
            List<ExamStudentDTO> list = new LinkedList<ExamStudentDTO>();
            for (ExamStudent student : query.getResult()) {
                if (student.isBreach() || student.isAbsent()) {
                    student.setObjectiveScore(0d);
                    student.setSubjectiveScore(0d);
                }
                list.add(new ExamStudentDTO(student));
            }
            new ExportExcel("成绩单", ExamStudentDTO.class).setDataList(list).write(response, fileName).dispose();
            return null;
        } catch (Exception e) {
            addMessage(redirectAttributes, "导出成绩失败！" + e.getMessage());
            return "redirect:/admin/exam/score";
        }
    }

    @RequestMapping("/enableExport")
    @ResponseBody
    public JSONObject query(HttpServletRequest request, @RequestParam(required = false) String subjectCode) {
        int examId = getSessionExamId(request);
        JSONObject obj = new JSONObject();
        String exportMessage = null;
        if (StringUtils.isNotBlank(subjectCode)) {
            exportMessage = enableExport(examId, subjectCode);
        } else {
            exportMessage = enableExport(examId);
        }
        if (exportMessage != null) {
            obj.accumulate("enableExport", false);
            obj.accumulate("exportMessage", exportMessage);
        } else {
            obj.accumulate("enableExport", true);
        }
        return obj;
    }

    @RequestMapping("/getProcess")
    @ResponseBody
    public JSONObject getProcess(HttpServletRequest request) {
        int examId = getSessionExamId(request);
        Exam exam = examService.findById(examId);
        boolean running = lockService.isLocked(LockType.SCORE_CALCULATE, examId);
        JSONObject obj = new JSONObject();
        obj.accumulate("running", running);
        if (exam.getProcess() != null) {
            DecimalFormat format = new DecimalFormat("##.##");
            obj.accumulate("process", format.format(exam.getProcess() * 100));
        } else {
            obj.accumulate("process", 0);
        }
        return obj;
    }

    private List<ScoreItem> buildScoreList(ExamStudent student) {
        List<ScoreItem> scoreList = new LinkedList<ScoreItem>();

        List<ExamQuestion> oList = questionService.findByExamAndSubjectAndObjectiveAndPaperType(student.getExamId(),
                student.getSubjectCode(), true, student.getPaperType());
        List<ScoreItem> list1 = student.getScoreList(true);
        int index = 0;
        for (ExamQuestion question : oList) {
            try {
                ScoreItem item = list1.get(index);
                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
                item.setMainNumber(question.getMainNumber());
                item.setSubNumber(question.getSubNumber());
            } catch (Exception e) {
            }
            index++;
        }
        scoreList.addAll(list1);

        List<ExamQuestion> sList = questionService.findByExamAndSubjectAndObjective(student.getExamId(),
                student.getSubjectCode(), false);
        List<ScoreItem> list2 = student.getScoreList(false);
        index = 0;
        for (ExamQuestion question : sList) {
            try {
                ScoreItem item = list2.get(index);
                item.setTitle(question.getMainTitle() + "-" + question.getSubNumber());
                item.setMainNumber(question.getMainNumber());
                item.setSubNumber(question.getSubNumber());
            } catch (Exception e) {
            }
            index++;
        }
        scoreList.addAll(list2);

        return scoreList;
    }

    private void buildSheetUrl(ExamStudent student) {
        // 改为内部原图浏览地址，直接附带标记内容
        student.setSheetUrls(PictureUrlBuilder.getInnerSheetUrls(student.getExamId(), student.getExamNumber(),
                student.getSheetCount()));
    }

    private void buildPackageUrl(ExamStudent student) {
        if (StringUtils.isNotBlank(student.getPackageCode())) {
            ExamPackage ep = packageService.find(student.getExamId(), student.getPackageCode());
            if (ep != null && ep.getPicCount() > 0) {
                student.setPackageUrls(PictureUrlBuilder.getPackageUrls(student.getExamId(), student.getPackageCode(),
                        ep.getPicCount()));
            }
        }
    }

    private void buildAnswerUrl(ExamStudent student) {
        ExamSubject subject = subjectService.find(student.getExamId(), student.getSubjectCode());
        if (subject != null && subject.isHasAnswer()) {
            student.setAnswerUrl(subject.getAnswerUrl());
        }
        if (subject != null) {
            student.setSubjectRemark(StringUtils.trimToEmpty(subject.getRemark()));
        }
    }

    private String enableExport(int examId) {
        String message = isChecked(examId);
        if (message == null) {
            List<ExamSubject> subjects = subjectService.list(examId);
            for (ExamSubject subject : subjects) {
                message = enableExport(examId, subject.getCode());
                if (message != null) {
                    return message;
                }
            }
        }
        return message;
    }

    private String isChecked(int examId) {
        String message = null;
        Exam exam = examService.findById(examId);
        // ExamStudentSearchQuery query = new ExamStudentSearchQuery();
        // query.setExamId(examId);
        // query.setUpload(false);
        // query.setManualAbsent(false);
        // long unUploadManualAbsentCount = studentService.countByQuery(query);
        if (exam.isNeedCalculate()) {
            message = "该考试需要统分";
        }
        // else if (unUploadManualAbsentCount > 0) {
        // message = "未上传考生必须人工指定缺考";
        // } else if (checkStudentService.countByExamIdAndChecked(examId, false)
        // != 0) {
        // message = "人工确认未完成";
        // }
        return message;
    }

    private String enableExport(int examId, String subjectCode) {
        String message = isChecked(examId);
        if (message == null) {
            List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(examId, subjectCode, MarkStatus.FORMAL,
                    MarkStatus.TRIAL);
            if (groups != null && !groups.isEmpty()) {
                message = subjectCode + " 科目未评卷完成";
            }
        }
        return message;
    }

}
