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

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

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.beans.factory.annotation.Value;
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.ExamSubjectDao;
import cn.com.qmth.stmms.biz.exam.model.ExamSubject;
import cn.com.qmth.stmms.biz.exam.service.ExamSubjectService;
import cn.com.qmth.stmms.biz.exam.service.query.ExamSubjectSearchQuery;
import cn.com.qmth.stmms.common.enums.ExamSubjectStatus;
import cn.com.qmth.stmms.common.enums.LibraryStatus;

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

    @Autowired
    private ExamSubjectDao subjectDao;

    @Value("${subject.name.bracket.replace}")
    private boolean replaceBracket;

    @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) {
        if (replaceBracket) {
            String subjectName = subject.getName().replaceAll("\\(", "（").replaceAll("\\)", "）");
            subject.setName(subjectName);
        }
        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<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 libraryCountGt, double subjectiveScoreGt) {
        return subjectDao.findByExamIdAndLibraryCountGtAndSubjectiveScoreGt(examId, libraryCountGt, subjectiveScoreGt);
    }

    @Override
    public List<ExamSubject> list(int examId, ExamSubjectStatus status, int libraryCountGt, double subjectiveScoreGt) {
        return subjectDao.findByExamIdAndStatusAndLibraryCountGtAndSubjectiveScoreGt(examId, status, libraryCountGt,
                subjectiveScoreGt);
    }

    @Override
    public List<ExamSubject> list(int examId, ExamSubjectStatus status, int libraryCountGt, int leftCountGt,
            double subjectiveScoreGt) {
        return subjectDao.findByExamIdAndStatusAndLibraryCountGtAndLeftCountGtAndSubjectiveScoreGt(examId, status,
                libraryCountGt, leftCountGt, subjectiveScoreGt);
    }

    @Override
    public List<ExamSubject> list(int examId, ExamSubjectStatus status) {
        if (status != null) {
            Set<ExamSubjectStatus> statusSet = new HashSet<ExamSubjectStatus>();
            statusSet.add(status);
            return subjectDao.findByExamIdAndStatus(examId, statusSet);
        } else {
            return list(examId);
        }
    }

    @Override
    public List<ExamSubject> list(int examId, Set<ExamSubjectStatus> statusSet) {
        if (statusSet != null && statusSet.size() > 0) {
            return subjectDao.findByExamIdAndStatus(examId, statusSet);
        } else {
            return list(examId);
        }
    }

    @Override
    public List<ExamSubject> list(Set<ExamSubjectStatus> statusSet) {
        if (statusSet != null && statusSet.size() > 0) {
            return subjectDao.findByStatus(statusSet);
        } else {
            return new LinkedList<ExamSubject>();
        }
    }

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

    @Override
    public long count(int examId, Set<ExamSubjectStatus> statusSet) {
        if (statusSet != null && statusSet.size() > 0) {
            return subjectDao.countByExamIdAndStatus(examId, statusSet);
        } else {
            return subjectDao.countByExamId(examId);
        }
    }

    @Override
    public long count(int examId, ExamSubjectStatus status) {
        if (status != null) {
            Set<ExamSubjectStatus> statusSet = new HashSet<ExamSubjectStatus>();
            statusSet.add(status);
            return subjectDao.countByExamIdAndStatus(examId, statusSet);
        } else {
            return subjectDao.countByExamId(examId);
        }
    }

    @Override
    public long sumLibraryCount(int examId, int libraryCountGt, double subjectiveScoreGt) {
        Long result = subjectDao.sumLibraryCountByExamIdAndLibraryCountGtAndSubjectiveScoreGt(examId, libraryCountGt,
                subjectiveScoreGt);
        return result != null ? result : 0;
    }

    @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 changeStatus(int examId, ExamSubjectStatus status) {
        subjectDao.updateStatusByExamId(examId, status);
    }

    @Transactional
    @Override
    public void updateLibraryCount(int examId) {
        subjectDao.updateLibraryCountByExamId(examId);
    }

    @Transactional
    @Override
    public void updateMarkedCount(int examId) {
        subjectDao.updateMarkedCountByExamId(examId, LibraryStatus.MARKED);
    }

    @Transactional
    @Override
    public void updateLeftCount(int examId) {
        subjectDao.updateLeftCountByExamId(examId);
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", key =
    // "T(String).valueOf(#examId)+'-'+#code", condition =
    // "#examId>0 && #code!=null", beforeInvocation = true)
    public void updateLibraryCount(int examId, String code, int libraryCount) {
        subjectDao.updateLibraryCountByExamIdAndCode(examId, code, libraryCount);
    }

    @Transactional
    @Override
    // @CacheEvict(value = "exam_subject_cache", key =
    // "T(String).valueOf(#examId)+'-'+#code", condition =
    // "#examId>0 && #code!=null", beforeInvocation = true)
    public void updateMarkedCount(int examId, String code, int markedCount) {
        subjectDao.updateMarkedCountByExamIdAndCode(examId, code, markedCount);
    }

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

    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.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status"), query.getStatus()));
                }
                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.getLibraryCountGt() != null) {
                    predicates.add(cb.gt(root.get("libraryCount").as(Integer.class), query.getLibraryCountGt()));
                }
                if (query.getLibraryCountEqual() != null) {
                    predicates.add(cb.equal(root.get("libraryCount"), query.getLibraryCountEqual()));
                }
                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        };
    }
    @Override
    public List<ExamSubject> findExamSubjectByMarkerId(int examId,int markerId ,LibraryStatus status){
        return subjectDao.findExamSubjectByMarkerId(examId,markerId,status.getValue());
    }
}
