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

import java.util.ArrayList;
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.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.MarkerDao;
import cn.com.qmth.stmms.biz.exam.model.Exam;
import cn.com.qmth.stmms.biz.exam.model.MarkGroup;
import cn.com.qmth.stmms.biz.exam.model.Marker;
import cn.com.qmth.stmms.biz.exam.query.MarkerSearchQuery;
import cn.com.qmth.stmms.biz.exam.service.ExamService;
import cn.com.qmth.stmms.biz.exam.service.MarkerService;
import cn.com.qmth.stmms.biz.mark.dao.MarkLibraryDao;
import cn.com.qmth.stmms.biz.user.dao.UserDao;
import cn.com.qmth.stmms.biz.user.model.User;
import cn.com.qmth.stmms.biz.user.service.UserService;
import cn.com.qmth.stmms.biz.user.service.query.UserSearchQuery;
import cn.com.qmth.stmms.common.enums.LibraryStatus;
import cn.com.qmth.stmms.common.enums.MarkStatus;

@Service("markerService")
public class MarkerServiceImpl extends BaseQueryService<Marker> implements MarkerService {

    @Autowired
    private MarkerDao markerDao;

    @Autowired
    private MarkLibraryDao libraryDao;

//    @Autowired
//    private MarkGroupService groupService;

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserService userService;

    @Autowired
    private ExamService examService;

    @Transactional
    @Override
    public Marker save(Marker marker) {
        return markerDao.save(marker);
    }

    @Override
    public Marker findById(Integer id) {
        return markerDao.findOne(id);
    }

    @Override
    public List<Marker> findByExamAndSubjectAndGroup(int examId, String subjectCode, int number) {
        return markerDao.findByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, number, null);
    }

    @Override
    public long countByExam(int examId) {
        return markerDao.countByExamId(examId);
    }

    @Override
    public long countByExamAndSubjectAndGroup(int examId, String subjectCode, int number) {
        return markerDao.countByExamIdAndSubjectCodeAndGroupNumber(examId, subjectCode, number);
    }

    @Override
    public List<Marker> getMarkCount(int examId) {
        List<Marker> list = new LinkedList<Marker>();
        List<Object[]> result = libraryDao.countMarkerAndStatus(examId, LibraryStatus.MARKED, LibraryStatus.INSPECTED);
        if (result != null) {
            for (Object[] array : result) {
                try {
                    Marker marker = findById((int) array[0]);
                    marker.setMarkedCount((long) array[1]);
                    list.add(marker);
                } catch (Exception e) {
                }
            }
        }
        return list;
    }

    @Override
    public List<Marker> getMarkCount(int examId, Set<String> subjectCodes) {
        List<Marker> list = new LinkedList<Marker>();
        List<Object[]> result = libraryDao.countMarkerAndStatusAndSubjectCodeIn(examId, subjectCodes,
                LibraryStatus.MARKED, LibraryStatus.INSPECTED);
        if (result != null) {
            for (Object[] array : result) {
                try {
                    Marker marker = findById((int) array[0]);
                    marker.setMarkedCount((long) array[1]);
                    list.add(marker);
                } catch (Exception e) {
                }
            }
        }
        return list;
    }

    @Override
    public MarkerSearchQuery findByQuery(final MarkerSearchQuery query) {
        checkQuery(query);
        Page<Marker> result = markerDao.findAll(buildSpecification(query), query);
        fillResult(result, query);
        return query;
    }

    private Specification<Marker> buildSpecification(final MarkerSearchQuery query) {
        return new Specification<Marker>() {

            @Override
            public Predicate toPredicate(Root<Marker> 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.getExamId() != null) {
                    predicates.add(cb.equal(root.get("examId"), query.getExamId()));
                }
                if (StringUtils.isNotBlank(query.getSubjectCode())) {
                    predicates.add(cb.equal(root.get("subjectCode"), query.getSubjectCode()));
                }
                if (StringUtils.isNotBlank(query.getSubjectCodeIn())) {
                    String[] list = query.getSubjectCodeIn().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("subjectCode"), list[i]);
                        }
                        predicates.add(cb.or(sub));
                    }
                }
                if (query.getGroupNumber() != null) {
                    predicates.add(cb.equal(root.get("groupNumber"), query.getGroupNumber()));
                }
                if (StringUtils.isNotBlank(query.getLoginName()) || StringUtils.isNotBlank(query.getName())) {
                    UserSearchQuery uQuery = new UserSearchQuery();
                    uQuery.setLoginName(query.getLoginName());
                    uQuery.setName(query.getName());
                    if (query.getExamId() != null) {
                        Exam exam = examService.findById(query.getExamId());
                        uQuery.setSchoolId(exam.getSchoolId());
                    }
                    List<User> users = userService.findByQuery(uQuery).getResult();
                    if (!users.isEmpty()) {
                        List<Integer> list = new ArrayList<Integer>();
                        for (User user : users) {
                            list.add(user.getId());
                        }
                        predicates.add(root.get("userId").in(list));
                    } else {
                        predicates.add(cb.equal(root.get("userId"), 0));
                    }
                }
                return predicates.isEmpty() ? cb.conjunction() : cb.and(predicates.toArray(new Predicate[predicates
                        .size()]));
            }
        };
    }

    @Override
    public int batchSave(List<Marker> list) {
        markerDao.save(list);
        return list.size();
    }

    @Override
    public Marker findByExamAndSubjectAndNumberAndUserId(int examId, String subjectCode, Integer number, Integer userId) {
        return markerDao.findByExamIdAndSubjectCodeAndGroupNumberAndUserId(examId, subjectCode, number, userId);
    }

    @Override
    public List<Marker> findByExamAndUserId(Integer examId, Integer userId) {
        return markerDao.findByExamIdAndUserIdAndGroupStatus(examId, userId, MarkStatus.TRIAL, MarkStatus.FORMAL);
    }

    @Override
    public long countByUserId(Integer userId) {
        return markerDao.countByUserId(userId);
    }

    @Transactional
    @Override
    public void deleteById(Integer markerId) {
        markerDao.delete(markerId);
    }

    @Override
    public void save(MarkGroup group, Integer[] userIds) {
        List<Marker> list = new ArrayList<Marker>();
        if (userIds != null && userIds.length > 0) {
            for (Integer userId : userIds) {
                Marker marker = this.findByExamAndSubjectAndNumberAndUserId(group.getExamId(), group.getSubjectCode(),
                        group.getNumber(), userId);
                if (marker == null) {
                    marker = new Marker();
                    marker.setExamId(group.getExamId());
                    marker.setSubjectCode(group.getSubjectCode());
                    marker.setGroupNumber(group.getNumber());
                    marker.setUserId(userId);
                    marker.setEnable(true);
                    list.add(marker);
                }
            }
        }
        this.batchSave(list);
    }

    @Override
    public List<Marker> findByExamAndSubjectAndUserIdAndEnable(Integer examId, String subjectCode, Integer userId,
            boolean enable) {
        return markerDao.findByExamIdAndSubjectCodeAndUserIdAndEnable(examId, subjectCode, userId, enable);
    }

    @Transactional
    @Override
    public void logoutByExamIdAndSubjectCodeAndGroupNumber(Integer examId, String subjectCode, Integer number) {
        List<Marker> markers = new ArrayList<Marker>();
        if (number == null) {
            markers = markerDao.findByExamIdAndSubjectCode(examId, subjectCode);
        } else {
            markers = this.findByExamAndSubjectAndGroup(examId, subjectCode, number);
        }
        List<User> list = new ArrayList<User>();
        for (Marker marker : markers) {
            User user = userDao.findOne(marker.getUserId());
            user.refreshAccessToken();
            list.add(user);
        }
        if (!list.isEmpty()) {
            userDao.save(list);
        }
    }

    @Override
    public void updateRejectCountById(Integer markerId) {
//        Marker marker = markerDao.findOne(markerId);
//        int rejectCount = 1;
//        if (marker.getRejectCount() != null) {
//            rejectCount = marker.getRejectCount() + 1;
//        }
//        markerDao.updateRejectCountById(markerId, rejectCount);
        markerDao.addRejectCountById(markerId);

    }

    @Transactional
    @Override
    public void updateEnableByUserId(Integer userId, boolean enable) {
        markerDao.updateEnableByUserId(userId, enable);
    }

}
