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

import cn.com.qmth.stmms.biz.common.BaseQueryService;
import cn.com.qmth.stmms.biz.exam.dao.ExamDao;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.query.ExamSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.mark.model.PictureConfigItem;
import cn.com.qmth.stmms.biz.mark.model.ProblemType;
import cn.com.qmth.stmms.biz.mark.service.ProblemTypeService;
import cn.com.qmth.stmms.common.enums.ExamStatus;
import cn.com.qmth.stmms.common.enums.ExamType;
import cn.com.qmth.stmms.common.enums.ObjectiveStatus;

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 javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import java.util.Date;
import java.util.LinkedList;
import java.util.List;

@Service
public class ExamServiceImpl extends BaseQueryService<Exam> implements ExamService {

    @Autowired
    private ExamDao examDao;

    @Autowired
    private ProblemTypeService problemService;

    @Value("${exam.problem.type1}")
    private String problemType1;

    @Value("${exam.problem.type2}")
    private String problemType2;

    @Override
    @Transactional
    // @CachePut(value = "exam_cache", key = "#exam.id", condition =
    // "#exam!=null && #exam.id!=null")
    public Exam save(Exam exam) {
        Integer id = exam.getId();
        if (id != null) {
            exam.setUpdateTime(new Date());
            examDao.save(exam);
        } else {
            exam.setCreateTime(new Date());
            examDao.save(exam);
            ProblemType p1 = new ProblemType(exam.getId(), problemType1, false);
            ProblemType p2 = new ProblemType(exam.getId(), problemType2, false);
            problemService.save(p1);
            problemService.save(p2);
        }
        return exam;
    }

    // @Cacheable(value = "exam_cache", key = "#id", condition = "#id!=null")
    public Exam findById(Integer id) {
        return examDao.findOne(id);
    }

    @Override
    @Transactional
    // @CacheEvict(value = "exam_cache", beforeInvocation = true, allEntries =
    // true)
    public void updateStatus(Integer id, ExamStatus status) {
        examDao.updateStatus(id, status);
    }

    @Override
    @Transactional
    // @CacheEvict(value = "exam_cache", beforeInvocation = true, allEntries =
    // true)
    public void updateSliceConfig(Integer id, List<PictureConfigItem> configList) {
        examDao.updateSliceConfig(id,
                configList != null ? StringUtils.join(configList, PictureConfigItem.DB_ITEM_JOINER) : "");
    }

    @Override
    public ExamSearchQuery findByQuery(final ExamSearchQuery query) {
        checkQuery(query);
        Page<Exam> result = examDao.findAll(new Specification<Exam>() {

            @Override
            public Predicate toPredicate(Root<Exam> root, CriteriaQuery<?> cQuery, CriteriaBuilder cb) {
                List<Predicate> predicates = new LinkedList<Predicate>();
                if (query.getId() != null) {
                    predicates.add(cb.equal(root.get("id"), query.getId()));
                }
                if (query.getSchoolId() != null) {
                    predicates.add(cb.equal(root.get("schoolId"), query.getSchoolId()));
                }
                if (query.getStatus() != null) {
                    predicates.add(cb.equal(root.get("status"), query.getStatus()));
                }
                if (query.getType() != null) {
                    predicates.add(cb.equal(root.get("type"), query.getType()));
                }
                if (StringUtils.isNotBlank(query.getName())) {
                    predicates.add(cb.like(root.get("name").as(String.class), query.getName() + "%"));
                }
                if (StringUtils.isNotBlank(query.getCode())) {
                    predicates.add(cb.equal(root.get("code"), query.getCode()));
                }

                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        }, query);
        fillResult(result, query);
        return query;
    }

    @Override
    public List<Exam> findBySchoolIdAndTypeAndStatus(Integer schoolId, ExamType type) {
        return examDao.findBySchoolIdAndTypeAndStatusOrderByIdDesc(schoolId, type, ExamStatus.START);
    }

    @Override
    public List<Exam> findBySchoolId(Integer schoolId) {
        return examDao.findBySchoolIdOrderByIdDesc(schoolId);
    }

    @Override
    public Exam findBySchoolAndCode(Integer schoolId, String code) {
        return examDao.findFirstBySchoolIdAndCode(schoolId, code);
    }

    @Override
    public List<Exam> findByMarkerUserId(Integer id, Date time) {
        return examDao.findByMarkerUserId(id, time);
    }

    @Override
    public List<Exam> findBySubjectHeaderUserId(Integer schoolId, Integer userId) {
        return examDao.findBySubjectHeaderUserId(schoolId, userId);
    }

    @Override
    @Transactional
    public int updateObjectiveStatus(int examId, ObjectiveStatus status) {
        return examDao.updateObjectiveStatus(examId, status);
    }

    @Override
    public List<Exam> findBySchoolViewUserId(Integer schoolId, Integer userId) {
        return examDao.findBySchoolViewUserId(schoolId, userId);
    }

    @Override
    @Transactional
    public int updateExamUpdateTime(int examId) {
        return examDao.updateUpdateTime(examId, new Date());
    }

}
