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

import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
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.dao.ExamStudentDao;
import cn.com.qmth.stmms.biz.exam.dao.ExamSubjectDao;
import cn.com.qmth.stmms.biz.exam.model.Exam;
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.CheckStudentService;
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.query.ExamSubjectSearchQuery;
import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
import cn.com.qmth.stmms.biz.utils.DoubleUtil;
import cn.com.qmth.stmms.common.enums.ExamType;
import cn.com.qmth.stmms.common.enums.MarkStatus;

@Service("examSubjectService")
public class ExamSubjectServiceImpl extends BaseQueryService<ExamSubject> implements ExamSubjectService {

    @Autowired
    private ExamSubjectDao subjectDao;

    @Autowired
    private ExamStudentDao examStudentDao;

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private MarkGroupService groupService;

    @Autowired
    private ExamStudentService studentService;

    @Autowired
    private CheckStudentService checkStudentService;

    @Transactional
    @Override
    // @CachePut(value = "exam_subject_cache", key =
    // "T(String).valueOf(#subject.examId)+'-'+#subject.code", condition =
    // "#subject!=null && #subject.examId!=null && #subject.code!=null")
    public ExamSubject save(ExamSubject subject) {
        return subjectDao.save(subject);
    }

    @Override
    // @Cacheable(value = "exam_subject_cache", key =
    // "T(String).valueOf(#examId)+'-'+#code", condition =
    // "#examId>0 && #code!=null")
    public ExamSubject find(int examId, String code) {
        return subjectDao.findByExamIdAndCode(examId, code);
    }

    @Override
    public List<ExamSubject> list(int examId) {
        return subjectDao.findByExamId(examId);
    }

    @Override
    public List<ExamSubject> list(int examId, Set<String> codes) {
        return subjectDao.findByExamIdAndSubjectCodeIn(examId, codes);
    }

    @Override
    public List<String> listLevel(int examId) {
        return subjectDao.findDistinctLevel(examId);
    }

    @Override
    public List<String> listCategory(int examId) {
        return subjectDao.findDistinctCategory(examId);
    }

    @Override
    public List<ExamSubject> list(int examId, int uploadCountGt) {
        return subjectDao.findByExamIdAndUploadCountGt(examId, uploadCountGt);
    }

    @Override
    public long count(int examId) {
        return subjectDao.countByExamId(examId);
    }

    @Override
    public ExamSubjectSearchQuery findByQuery(ExamSubjectSearchQuery query) {
        checkQuery(query);
        Page<ExamSubject> result = subjectDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    @Override
    public long countByQuery(ExamSubjectSearchQuery query) {
        return subjectDao.count(buildSpecification(query));
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", beforeInvocation = true,
    // allEntries = true)
    public void delete(ExamSubject markSubject) {
        subjectDao.delete(markSubject);
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", beforeInvocation = true,
    // allEntries = true)
    public void deleteByExamId(int examId) {
        subjectDao.deleteByExamId(examId);
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", beforeInvocation = true,
    // allEntries = true)
    public void updateTotalScore(int examId) {
        subjectDao.updateTotalScoreByExamId(examId);
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", beforeInvocation = true,
    // allEntries = true)
    public void updateScore(int examId, String code, boolean objective, double score) {
        if (objective) {
            subjectDao.updateObjectiveScore(examId, code, score);
        } else {
            subjectDao.updateSubjectiveScore(examId, code, score);
        }
        subjectDao.updateTotalScoreByExamIdAndCode(examId, code);
    }

    private Specification<ExamSubject> buildSpecification(final ExamSubjectSearchQuery query) {
        return new Specification<ExamSubject>() {

            @Override
            public Predicate toPredicate(Root<ExamSubject> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new LinkedList<Predicate>();
                if (query.getExamId() > 0) {
                    predicates.add(cb.equal(root.get("pk").get("examId"), query.getExamId()));
                }
                if (StringUtils.isNotEmpty(query.getCode())) {
                    predicates.add(cb.equal(root.get("pk").get("code"), query.getCode()));
                }
                if (StringUtils.isNotBlank(query.getLevel())) {
                    predicates.add(cb.equal(root.get("level"), query.getLevel()));
                }
                if (StringUtils.isNotBlank(query.getCategory())) {
                    predicates.add(cb.equal(root.get("category"), query.getCategory()));
                }
                if (query.getTotalScore() != null) {
                    predicates.add(cb.equal(root.get("totalScore"), query.getTotalScore()));
                }
                if (query.getTotalScoreNotEqual() != null) {
                    predicates.add(cb.notEqual(root.get("totalScore"), query.getTotalScoreNotEqual()));
                }
                if (query.getSubjectiveScoreNotEqual() != null) {
                    predicates.add(cb.notEqual(root.get("subjectiveScore"), query.getSubjectiveScoreNotEqual()));
                }
                if (query.getUploadCountGt() != null) {
                    predicates.add(cb.gt(root.get("uploadCount").as(Integer.class), query.getUploadCountGt()));
                }
                if (query.getUploadCountEqual() != null) {
                    predicates.add(cb.equal(root.get("uploadCount").as(Integer.class), query.getUploadCountEqual()));
                }
                if (query.getSelective() != null) {
                    predicates.add(cb.equal(root.get("selective"), query.getSelective()));
                }
                if (StringUtils.isNotBlank(query.getCodeIn())) {
                    String[] list = query.getCodeIn().split(",");
                    if (list.length > 0) {
                        Predicate[] sub = new Predicate[list.length];
                        for (int i = 0; i < list.length; i++) {
                            sub[i] = cb.equal(root.get("pk").get("code"), list[i]);
                        }
                        predicates.add(cb.or(sub));
                    }
                }
                if (StringUtils.isNotBlank(query.getCodeNotIn())) {
                    String[] list = query.getCodeNotIn().split(",");
                    if (list.length > 0) {
                        Predicate[] sub = new Predicate[list.length];
                        for (int i = 0; i < list.length; i++) {
                            sub[i] = cb.notEqual(root.get("pk").get("code"), list[i]);
                        }
                        predicates.add(cb.and(sub));
                    }
                }
                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        };
    }

    @Override
    @Transactional
    public void updateUploadCount(Integer examId, String subjectCode, int count) {
        subjectDao.updateUploadCount(examId, subjectCode, count);
    }

    @Override
    public List<ExamSubject> listSubjectiveScore(int examId, double subjectiveScore) {
        return subjectDao.listSubjectiveScore(examId, subjectiveScore);
    }

    @Override
    @Transactional
    public void updateSliceConfig(Integer examId, String subjectCode, List<PictureConfigItem> configList) {
        subjectDao.updateSliceConfig(examId, subjectCode,
                configList != null ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) : "");
    }

    @Transactional
    @Override
    public void updateTrialCount(int examId, String subjectCode, int trialCount) {
        subjectDao.updateTrialCount(examId, subjectCode, trialCount);
    }

    @Override
    public int findMaxInspectRound(int examId, Set<String> subjectCodes) {
        StringBuilder sql = new StringBuilder("select max(s.inspect_round) from eb_exam_subject s ");
        sql.append(" where s.exam_id=" + examId + " and s.code in ('" + StringUtils.join(subjectCodes, "','") + "')");
        Query countQuery = entityManager.createNativeQuery(sql.toString());
        Object singleResult = countQuery.getResultList().get(0);
        Integer count = singleResult == null ? 0 : Integer.valueOf(singleResult.toString());
        return count;
    }

    @Transactional
    @Override
    public void nextInspectRound(int examId, String subjectCode) {
        subjectDao.nextInspectRound(examId, subjectCode);
        examStudentDao.cancelInspect(examId, subjectCode,false,true);
    }

    @Transactional
    @Override
    public void updateSelective(int examId, String subjectCode, boolean selective) {
        subjectDao.updateSelective(examId, subjectCode, selective);
    }

    @Override
    public String enableExport(Exam exam, String subjectCode) {
        ExamSubject subject = find(exam.getId(), subjectCode);
        if (subject == null) {
            return subjectCode + "该科目不存在";
        }
        List<MarkGroup> groups = groupService.findByExamAndSubjectAndStatus(exam.getId(), subjectCode,
                MarkStatus.FORMAL, MarkStatus.TRIAL);
        if (groups != null && !groups.isEmpty()) {
            return subjectCode + "评卷未完成";
        }
        if (ExamType.MULTI_MEDIA.equals(exam.getType())) {
            return null;
        }
        if (DoubleUtil.sum(subject.getObjectiveScore(), subject.getSubjectiveScore()) == 0) {
            return subjectCode + "科目总分为0";
        }
        long groupCount = groupService.countByExamAndSubject(exam.getId(), subjectCode);
        if (subject.getSubjectiveScore() != null && subject.getSubjectiveScore() > 0 && groupCount == 0) {
            return subjectCode + "未设置评卷分组";
        }
        if (checkStudentService.countByExamIdAndSubjectCodeAndChecked(exam.getId(), subjectCode, false) > 0) {
            return subjectCode + "人工确认未完成";
        }
        ExamStudentSearchQuery query = new ExamStudentSearchQuery();
        query.setExamId(exam.getId());
        query.setUpload(false);
        query.setManualAbsent(false);
        query.setSubjectCode(subjectCode);
        if (studentService.countByQuery(query) > 0) {
            StringBuilder builder = new StringBuilder(subjectCode + "未上传考生必须人工指定缺考");
            query.setPageSize(Integer.MAX_VALUE);
            query = studentService.findByQuery(query);
            for (ExamStudent s : query.getResult()) {
                builder.append("," + s.getExamNumber());
            }
            return builder.toString();
        }
        query.setUpload(true);
        query.setManualAbsent(true);
        if (studentService.countByQuery(query) > 0) {
            StringBuilder builder = new StringBuilder(subjectCode + "已上传考生有人工指定缺考");
            query.setPageSize(Integer.MAX_VALUE);
            query = studentService.findByQuery(query);
            for (ExamStudent s : query.getResult()) {
                builder.append("," + s.getExamNumber());
            }
            return builder.toString();
        }
        return null;
    }

    @Override
    public List<Double> distinctTotalScore(int examId) {
        return subjectDao.distinctTotalScore(examId);
    }

    @Transactional
	@Override
	public void resetInspect(int examId, String subjectCode) {
		subjectDao.resetInspect(examId,subjectCode);
	}

}
