package com.apobates.forum.core.impl.dao;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.apobates.forum.core.api.dao.PostsMoodRecordsDao;
import com.apobates.forum.core.entity.PostsMoodRecords;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Repository
public class PostsMoodRecordsDaoImpl implements PostsMoodRecordsDao{
	@PersistenceContext
	private EntityManager entityManager;
	private final static Logger logger = LoggerFactory.getLogger(PostsMoodRecordsDaoImpl.class);
	
	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public void save(PostsMoodRecords entity) {
		entityManager.persist(entity);
	}

	@Override
	public Optional<PostsMoodRecords> findOne(Long primaryKey) {
		return Optional.empty();
	}

	@Override
	public Optional<Boolean> edit(PostsMoodRecords updateEntity) {
		return Optional.empty();
	}

	@Override
	public Stream<PostsMoodRecords> findAll() {
		return Stream.empty();
	}

	@Override
	public long count() {
		return -1L;
	}

	@Override
	public Page<PostsMoodRecords> findAll(Pageable pageable) {
		return emptyResult();
	}

	@Override
	public Page<PostsMoodRecords> findAllByPosts(long postsId, Pageable pageable) {
		final long total = findAllByPostsCount(postsId);
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<PostsMoodRecords> query = entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId = ?1 ORDER BY pm.entryDateTime DESC", PostsMoodRecords.class).setParameter(1, postsId);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

		final Stream<PostsMoodRecords> result = query.getResultStream();
		return new Page<PostsMoodRecords>() {
			@Override
			public long getTotalElements() {
				return total;
			}

			@Override
			public Stream<PostsMoodRecords> getResult() {
				return result;
			}
		};
	}
	
	private long findAllByPostsCount(long postsId) {
		try {
			return entityManager.createQuery("SELECT COUNT(pm) FROM PostsMoodRecords pm WHERE pm.postsId = ?1", Long.class).setParameter(1, postsId).getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[findAllByPostsCount][PostsMoodRecordsDao]", e);
			}
		}
		return 0L;
	}
	
	@Override
	public Stream<PostsMoodRecords> findAllByMood(long postsId, boolean liked) {
		return entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId = ?1 AND pm.liked = ?2", PostsMoodRecords.class)
				.setParameter(1, postsId)
				.setParameter(2, liked)
				.getResultStream();
	}

	@Override
	public Map<Boolean, Long> collectMembers(long postsId) {
		Map<Boolean, Long> data = new HashMap<>();
		List<Object[]> results = entityManager.createQuery("SELECT pm.liked, COUNT(pm) FROM PostsMoodRecords pm WHERE pm.postsId = ?1 GROUP BY pm.liked").setParameter(1, postsId).getResultList();
		for (Object[] result : results) {
			Boolean name = (Boolean) result[0];
			Long count = 0L;
			
			try{
				count = ((BigDecimal)result[1]).longValue();
			}catch(java.lang.ClassCastException e){
				count = (Long)result[1];
			}
			data.put(name, count);
		}
		return data;
	}

	@Override
	public Map<Long, Map<Boolean, Long>> collectMembers(Collection<Long> postsIdSet) {
		if(postsIdSet == null || postsIdSet.isEmpty()){
			return Collections.emptyMap();
		}
		Stream<PostsMoodRecords> rs = entityManager.createQuery("SELECT pm FROM PostsMoodRecords pm WHERE pm.postsId IN ?1", PostsMoodRecords.class).setParameter(1, postsIdSet).getResultStream();
		Map<Long,List<PostsMoodRecords>> data = rs.collect(Collectors.groupingBy(PostsMoodRecords::getPostsId));
		//
		Map<Long, Map<Boolean, Long>> result = new HashMap<>();
		for(Entry<Long,List<PostsMoodRecords>> entry:data.entrySet()){
			Long postsId = entry.getKey();
			Map<Boolean, Long> stats = entry.getValue().stream().collect(Collectors.groupingBy(PostsMoodRecords::isLiked, Collectors.counting()));
			result.put(postsId, stats);
		}
		return result;
	}

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public Optional<Boolean> removeMoodRecord(long postsId, long memberId) {
		int affect = entityManager.createQuery("DELETE FROM PostsMoodRecords pm WHERE pm.postsId = ?1 AND pm.memberId = ?2").setParameter(1, postsId).setParameter(2, memberId).executeUpdate();
		return affect == 1?Optional.of(true):Optional.empty();
	}
}
