package cn.com.qmth.stmms.biz.exam.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.hibernate.SQLQuery;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.com.qmth.stmms.biz.common.BaseQueryService;
import cn.com.qmth.stmms.biz.exam.bean.ExamStudentVo;
import cn.com.qmth.stmms.biz.exam.model.ExamStudent;
import cn.com.qmth.stmms.biz.exam.model.InspectHistory;
import cn.com.qmth.stmms.biz.exam.query.ExamStudentSearchQuery;
import cn.com.qmth.stmms.biz.exam.query.InspectQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamStudentService;
import cn.com.qmth.stmms.biz.exam.service.InspectHistoryService;
import cn.com.qmth.stmms.biz.exam.service.InspectedService;
import cn.com.qmth.stmms.biz.utils.TaskLock;
import cn.com.qmth.stmms.biz.utils.TaskLockUtil;
import cn.com.qmth.stmms.common.enums.SelectiveStatus;
import cn.com.qmth.stmms.common.enums.SubjectiveStatus;

@Service
public class InspectedServiceImpl extends BaseQueryService<ExamStudent> implements InspectedService {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private ExamStudentService studentService;


    @Autowired
    private InspectHistoryService inspectHistoryService;

    @SuppressWarnings("unchecked")
    @Override
    public List<ExamStudentVo> findByQuery(InspectQuery query, SubjectiveStatus status, Integer mainNumber,
            Double mainStartScore, Double mainEndScore, SelectiveStatus selectiveStatus) {
        int offset = (query.getPageNumber() - 1) * query.getPageSize();
        StringBuilder sql = new StringBuilder();
        sql.append(" select s.id,s.exam_id examId,s.school_id schoolId,s.subject_code subjectCode,");
        sql.append(" s.subject_name subjectName,s.paper_type paperType,s.exam_number examNumber,s.secret_number secretNumber,");
        sql.append(" s.student_code studentCode,s.name,s.package_code packageCode,s.campus_name campusName,s.exam_site examSite,");
        sql.append(" s.exam_room examRoom,s.remark,s.batch_code batchCode,s.sheet_count sheetCount,s.slice_count sliceCount,s.answers,");
        sql.append(" s.is_upload upload,s.is_absent absent,s.is_manual_absent manualAbsent,s.is_breach breach,s.is_exception exception,");
        sql.append(" s.upload_time uploadTime,s.objective_score objectiveScore,s.objective_score_list objectiveScoreList,");
        sql.append(" s.subjective_status subjectiveStatus,s.subjective_score subjectiveScore,s.subjective_score_list subjectiveScoreList,");
        sql.append(" s.subject_level subjectLevel,s.subject_category subjectCategory,s.college,s.class_name className,s.teacher,");
        sql.append(" s.card_number cardNumber,s.inspect_count inspectCount,s.score_verify_user scoreVerifyUser,");
        sql.append(" s.score_verify_time scoreVerifyTime,s.score_verify_flagged scoreVerifyFlagged,s.inspect_time inspectTime,");
        sql.append(" s.inspector_id inspectorId,s.inspected ");
        sql.append(" from eb_exam_student s left join eb_exam_subject es on s.exam_id=es.exam_id and s.subject_code=es.code ");
        String whereSql = getWhereSql(query, status, mainNumber, mainStartScore, mainEndScore, selectiveStatus);
        sql.append(whereSql).append(" order by s.inspect_time desc,s.id asc");
        if (!query.isExport()) {
            sql.append(" limit " + offset + "," + query.getPageSize());
        }
        Query dataQuery = entityManager.createNativeQuery(sql.toString());
        dataQuery.unwrap(SQLQuery.class).setResultTransformer(Transformers.aliasToBean(ExamStudentVo.class));
        List<ExamStudentVo> resultList = dataQuery.getResultList();
        return resultList;

    }

    private String getWhereSql(InspectQuery query, SubjectiveStatus status, Integer mainNumber, Double mainStartScore,
            Double mainEndScore, SelectiveStatus selectiveStatus) {
        StringBuilder whereSql = new StringBuilder(" WHERE s.is_upload = 1 and s.is_absent = 0 and s.is_breach = 0 ");
        if (query.getExamId() != null) {
            whereSql.append(" and s.exam_id =" + query.getExamId());
        }
        if (StringUtils.isNotBlank(query.getSubjectCode())) {
            whereSql.append(" and s.subject_code ='" + query.getSubjectCode() + "'");
        }
        if (query.getSelective() != null) {
            whereSql.append(" and es.selective =" + (query.getSelective() ? 1 : 0));
        }
        if (StringUtils.isNotBlank(query.getInspectorName())) {
            whereSql.append(" and exists (select 1 from eb_inspect_history ei left join b_user bu on ei.inspector_id=bu.id "
                    + " where ei.student_id = s.id and (bu.login_name like '"
                    + query.getInspectorName()
                    + "%' or bu.name like '" + query.getInspectorName() + "%'))");
        }
//        if (query.getInspectUnrepeated() != null && query.getInspectUnrepeated()) {
//            whereSql.append(" and not exists (select 1 from eb_inspect_history ei where ei.student_id = s.id and ei.inspector_id="
//                    + query.getInspectorId() + ")");
//        }
        if (StringUtils.isNotBlank(query.getSubjectCodeIn())) {
            String[] subjectCodeIn = query.getSubjectCodeIn().split(",");
            List<String> in = new ArrayList<String>();
            for (String code : subjectCodeIn) {
                in.add(code);
            }
            whereSql.append(" and s.subject_code in ('" + StringUtils.join(in, "','") + "') ");
        }
        if (query.getRejected() != null) {
        	if (query.getRejected()) {
        		whereSql.append(" and exists (select 1 from eb_subjective_score ess where ess.student_id=s.id and rejected=1) ");
        	}else {
        		whereSql.append(" and not exists (select 1 from eb_subjective_score ess where ess.student_id=s.id and rejected=1) ");
        	}
        }
        whereSql.append(" and s.subjective_status ='MARKED'");
//        if (query.getInspected() != null) {
//            whereSql.append(" and s.inspected =" + (query.getInspected() ? 1 : 0));
//        }
        if (query.getStartScore() != null) {
            whereSql.append(" and (cast(s.objective_score as decimal(10,3))+cast(s.subjective_score as decimal(10,3))) >="
                    + query.getStartScore());
        }
        if (query.getEndScore() != null) {
            whereSql.append(" and (cast(s.objective_score as decimal(10,3))+cast(s.subjective_score as decimal(10,3))) <="
                    + query.getEndScore());
        }
        if (StringUtils.isNotBlank(query.getSecretNumber())) {
            whereSql.append(" and s.secret_number ='" + query.getSecretNumber() + "'");
        }
        Double questionScore = query.getQuestionScore();
        if (mainNumber != null || mainStartScore != null || mainEndScore != null || questionScore != null) {
            whereSql.append(" and exists (select e.student_id from eb_subjective_score e where e.student_id = s.id ");
            if (mainNumber != null) {
                whereSql.append(" and e.main_number =" + mainNumber);
            }
            if (mainStartScore != null) {
                whereSql.append(" and e.main_score >=" + mainStartScore);
            }
            if (mainEndScore != null) {
                whereSql.append(" and e.main_score <=" + mainEndScore);
            }
            if (questionScore != null) {
                if (questionScore != 0) {
                    whereSql.append(" and e.score =" + questionScore);
                } else {
                    if (query.getQuestionScoreEmpty() != null) {
                        if (query.getQuestionScoreEmpty()) {
                            whereSql.append(" and e.score =0 and e.unanswered_count >0");
                        } else {
                            whereSql.append(" and e.score =0 and (e.unanswered_count is null or e.unanswered_count =0)");
                        }
                    } else {
                        whereSql.append(" and e.score =0");
                    }
                }
            }
            whereSql.append(" ) ");
        }
        // 全部选做题为未选做
        // List<MarkGroup> groups = groupService
        // .findByExamAndSubjectAndSelective(query.getExamId(),
        // query.getSubjectCode(), true);
        // List<SelectiveGroup> selectiveGroups = selectiveGroupService
        // .findByExamIdAndSubjectCode(query.getExamId(),
        // query.getSubjectCode());
        // if (selectiveStatus != null &&
        // SelectiveStatus.UN_SELECTIVE.equals(selectiveStatus) &&
        // !groups.isEmpty()
        // && !selectiveGroups.isEmpty()) {
        // List<Integer> groupNumbers = new ArrayList<Integer>();
        // for (int i = 0; i < groups.size(); i++) {
        // groupNumbers.add(groups.get(i).getNumber());
        // }
        // whereSql.append(
        // " and not exists (select e.student_id from eb_subjective_score e where e.student_id = s.id and e.group_number in ("
        // +StringUtils.join(groupNumbers,",")+") and e.group_score != -1 )");
        // }
        if (SelectiveStatus.UN_SELECTIVE.equals(selectiveStatus)) {
//            whereSql.append(" and not exists (select 1 from eb_subjective_score e left join eb_mark_group mg "
//                    + " on e.exam_id=mg.exam_id and e.subject_code=mg.subject_code and e.group_number=mg.number"
//                    + " where e.student_id = s.id  and e.group_score != -1 and mg.is_selective=1)"
//                    + " and es.selective=1");
            whereSql.append(" and exists (select ss.student_id from eb_selective_student ss where ss.student_id = s.id and ss.not_selective=1)");
        }
        // 选做题多选做
        // if (selectiveStatus != null &&
        // SelectiveStatus.MUTI_SELECTIVE.equals(selectiveStatus) &&
        // !groups.isEmpty()
        // && !selectiveGroups.isEmpty()) {
        if (SelectiveStatus.MUTI_SELECTIVE.equals(selectiveStatus)) {
            whereSql.append(" and exists (select ss.student_id from eb_selective_student ss where ss.student_id = s.id and ss.muti_selective=1)");
        }
        // 选做题少选做
        // if (selectiveStatus != null &&
        // SelectiveStatus.LESS_SELECTIVE.equals(selectiveStatus) &&
        // !groups.isEmpty()
        // && !selectiveGroups.isEmpty()) {
        if (SelectiveStatus.LESS_SELECTIVE.equals(selectiveStatus)) {
            whereSql.append(" and exists (select ss.student_id from eb_selective_student ss where ss.student_id = s.id and ss.less_selective=1) ");
        }
        return whereSql.toString();
    }

    @Override
    public Integer countByQuery(InspectQuery query, SubjectiveStatus status, Integer mainNumber, Double mainStartScore,
            Double mainEndScore, SelectiveStatus selectiveStatus) {
        StringBuilder countSql = new StringBuilder("select count(s.id) from eb_exam_student s  "
                + " left join eb_exam_subject es on s.exam_id=es.exam_id and s.subject_code=es.code ");
        String whereSql = getWhereSql(query, status, mainNumber, mainStartScore, mainEndScore, selectiveStatus);
        countSql.append(whereSql);
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        Object singleResult = countQuery.getResultList().get(0);
        Integer count = singleResult == null ? 0 : Integer.valueOf(singleResult.toString());
        return count;
    }

    @Override
    public Integer needInspectCount(InspectQuery query, Integer mainNumber, Double mainStartScore, Double mainEndScore,
            SelectiveStatus selectiveStatus) {
        Boolean inspected = query.getInspected();
        query.setInspected(false);
        StringBuilder countSql = new StringBuilder("select count(s.id) from eb_exam_student s "
                + " left join eb_exam_subject es on s.exam_id=es.exam_id and s.subject_code=es.code ");
        String whereSql = getWhereSql(query, SubjectiveStatus.MARKED, mainNumber, mainStartScore, mainEndScore,
                selectiveStatus);
        countSql.append(whereSql);
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        Object singleResult = countQuery.getResultList().get(0);
        Integer count = singleResult == null ? 0 : Integer.valueOf(singleResult.toString());
        query.setInspected(inspected);
        return count;
    }
    
    @SuppressWarnings("unchecked")
	@Override
    public List<Integer> needInspectIds(InspectQuery query, Integer mainNumber, Double mainStartScore, Double mainEndScore,
            SelectiveStatus selectiveStatus) {
    	query.setInspected(null);
        StringBuilder countSql = new StringBuilder("select s.id from eb_exam_student s "
                + " left join eb_exam_subject es on s.exam_id=es.exam_id and s.subject_code=es.code ");
        String whereSql = getWhereSql(query, SubjectiveStatus.MARKED, mainNumber, mainStartScore, mainEndScore,
                selectiveStatus);
        countSql.append(whereSql).append(" order by s.inspect_time desc,s.id asc");
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        List<Integer> ret = countQuery.getResultList();
        if(ret==null) {
        	return new ArrayList<>();
        }
        return ret;
    }

    @Override
    public void releaseByUserId(Integer examId, String subjectCode, Integer userId) {
        String key = examId + "_" + subjectCode;
        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(key);
        taskLock.clear(userId);
    }

    @Override
    public void releaseByStudent(ExamStudent student) {
        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
        taskLock.remove(student.getId(), 1);
    }

    @Override
    public boolean applyStudent(ExamStudentVo student, Integer userId) {
        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
        boolean lock = taskLock.add(student.getId(), 1, userId);
        // 上锁失败直接返回
        if (!lock) {
            return false;
        }
        // 重复校验任务状态
        if (student.getSubjectiveStatus().equals(SubjectiveStatus.MARKED)) {
            return true;
        } else {
            taskLock.remove(student.getId(), 1, userId);
            return false;
        }
    }

    @Override
    public boolean hasApplied(ExamStudent student, Integer userId) {
        TaskLock taskLock = TaskLockUtil.getInspectedStudentTask(getKey(student));
        return taskLock.exist(student.getId(), 1, userId);
    }

    private String getKey(ExamStudentVo student) {
        return student.getExamId() + "_" + student.getSubjectCode();
    }

    private String getKey(ExamStudent student) {
        return student.getExamId() + "_" + student.getSubjectCode();
    }

    @Transactional
    @Override
    public boolean inspect(ExamStudent student, Integer userId) {
        if (this.hasApplied(student, userId) && SubjectiveStatus.MARKED.equals(student.getSubjectiveStatus())) {
        	if (!SubjectiveStatus.MARKED.equals(student.getSubjectiveStatus())) {
                return false;
            }
//            if (!SubjectiveStatus.MARKED.equals(student.getSubjectiveStatus()) || student.getInspected()) {
//                return false;
//            }
//            ExamSubject subject = examSubjectService.find(student.getExamId(), student.getSubjectCode());
//            InspectHistory old = inspectHistoryService.findByStudentIdAndInspectRound(student.getId(),
//                    subject.getInspectRound());
//            if (old != null) {
//                return false;
//            }
            Date now = new Date();
            // studentService.inspect(student.getId(), now, userId);
            InspectHistory i = new InspectHistory();
            i.setExamId(student.getExamId());
            i.setStudentId(student.getId());
            i.setSubjectCode(student.getSubjectCode());
            i.setInspectorId(userId);
            i.setInspectTime(now);
            inspectHistoryService.save(i);
            studentService.updateInspectCount(student.getId(), inspectHistoryService.countByStudentId(student.getId()),
                    now, userId);
            // this.releaseByStudent(student);
            return true;
        }
        return false;
    }

    /**
     * 清除当前轮的复核，以及复核状态
     */
    @Transactional
    @Override
    public boolean cancelByStudent(Integer studentId) {
//        if (studentService.cancelInspect(studentId) > 0) {
//            inspectHistoryService.deleteByStudentIdAndInspectRound(studentId);
//        }
        return true;
    }

    @Transactional
    @Override
    public void clearByStudent(Integer studentId) {
        studentService.cancelInspect(studentId);
        inspectHistoryService.deleteByStudentId(studentId);
    }

    @Override
    public Integer fillInspectHistoryMap(List<ExamStudentVo> list) {
    	Integer maxCount=0;
        List<Integer> sids = list.stream().map(e -> e.getId()).collect(Collectors.toList());
        List<InspectHistory> ihs = inspectHistoryService.findByStudentIdIn(sids);
        if (ihs == null || ihs.size() == 0) {
            return maxCount;
        }
        Map<Integer, Map<String, InspectHistory>> map = new HashMap<>();
        for (InspectHistory ih : ihs) {
            Map<String, InspectHistory> tem = map.get(ih.getStudentId());
            if (tem == null) {
                tem = new HashMap<>();
                map.put(ih.getStudentId(), tem);
            }
            tem.put((tem.size()+1)+"", ih);
            if(tem.size()>maxCount) {
            	maxCount=tem.size();
            }
        }
        for (ExamStudentVo es : list) {
            es.setInspectHistoryMap(map.get(es.getId()));
        }
        return maxCount;
    }

    @Override
    public boolean inspectFinish(Integer examId, String subjectCode, Integer inspectRound) {
        ExamStudentSearchQuery sQuery = new ExamStudentSearchQuery();
        sQuery.setExamId(examId);
        sQuery.setUpload(true);
        sQuery.setAbsent(false);
        sQuery.setBreach(false);
        sQuery.setSubjectCode(subjectCode);
        long totalPaperCount = studentService.countByQuery(sQuery);
        StringBuilder countSql = new StringBuilder("select count(s.id) from eb_exam_student s  where s.exam_id ="
                + examId + " and s.subject_code ='" + subjectCode + "'");
        countSql.append(" and s.inspected=1");
        // countSql.append(" and exists(select 1 from eb_inspect_history h "
        // +" left join eb_exam_subject sub on h.exam_id=sub.exam_id and h.subject_code=sub.code "
        // +" where s.id=h.student_id and h.inspect_round="+inspectRound+") ");
        Query countQuery = entityManager.createNativeQuery(countSql.toString());
        Object singleResult = countQuery.getResultList().get(0);
        Integer count = singleResult == null ? 0 : Integer.valueOf(singleResult.toString());
        return totalPaperCount == count;
    }

    @Transactional
    @Override
    public void cancelBySubject(Integer examId, String subjectCode) {
        studentService.cancelInspect(examId, subjectCode);
        inspectHistoryService.deleteByExamIdAndSubjectCode(examId, subjectCode);
    }
}
