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

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Optional;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.apobates.forum.core.api.dao.TopicTagDao;
import com.apobates.forum.core.entity.TopicTag;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Repository
public class TopicTagDaoImpl implements TopicTagDao {
	@PersistenceContext
	private EntityManager entityManager;
	@Value("${jpa.batch.size}")
	private int batchSize;
	private final static Logger logger = LoggerFactory.getLogger(TopicTagDaoImpl.class);

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public void save(TopicTag entity) {
		entityManager.persist(entity);
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public int batchSave(long topicId, Map<String, Integer> tages) {
		if(tages == null || tages.isEmpty()){
			return -1; //.failure("参数不合法或不被接受");
		}
		Set<TopicTag> rs = new HashSet<>();
		for(Entry<String,Integer> entry : tages.entrySet()){
			rs.add(new TopicTag(entry.getKey(), entry.getValue(), topicId));
		}
		int affect = batchSave(topicId, rs);
		return affect;
	}

	@Override
	public Optional<TopicTag> findOne(Long primaryKey) {
		return Optional.ofNullable(entityManager.find(TopicTag.class, primaryKey));
	}

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

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

	@Override
	public long count() {
		try {
			return entityManager.createQuery("SELECT COUNT(tt) FROM TopicTag tt", Long.class).getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[count][TopicTagDao]", e);
			}
		}
		return 0L;
	}

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public int batchSave(long topicId, Collection<TopicTag> tages) {
		if(null == tages || tages.isEmpty()){
			return 0;
		}
		//删除掉原来的标签
		int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.topicId = ?1").setParameter(1, topicId).executeUpdate();
		logger.debug("[batchSave][TopicTagDao] delete exists tag size:"+affect);
		int i = 0;
		for (TopicTag tt : tages) {
			entityManager.persist(tt);
			i++;
			if (i % batchSize == 0) {
				// Flush a batch of inserts and release memory.
				entityManager.flush();
				entityManager.clear();
			}
		}
		return i;
	}

	@Override
	public List<TopicTag> findAllByTopic(long topicId) {
		return entityManager.createQuery("SELECT tt FROM TopicTag AS tt WHERE tt.topicId = ?1 AND tt.status = ?2", TopicTag.class).setParameter(1, topicId).setParameter(2, true).getResultList();
	}

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public Optional<Boolean> remove(long id) {
		int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.id = ?1").setParameter(1, id).executeUpdate();
		return affect == 1?Optional.of(true):Optional.empty();
	}

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public Optional<Boolean> remove(long id, long topicId) {
		int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.id = ?1 AND tt.topicId = ?2").setParameter(1, id).setParameter(2, topicId).executeUpdate();
		return affect == 1?Optional.of(true):Optional.empty();
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public int removeAll(long topicId) {
		int affect = entityManager.createQuery("DELETE FROM TopicTag tt WHERE tt.topicId = ?1").setParameter(1, topicId).executeUpdate();
		return affect;
	}

	@Override
	public Page<TopicTag> findAll(Pageable pageable) {
		final long total = count();
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<TopicTag> query = entityManager.createQuery("SELECT tt FROM TopicTag tt ORDER BY tt.id DESC", TopicTag.class);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

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

			@Override
			public Stream<TopicTag> getResult() {
				return result;
			}
		};
	}

	@Override
	public Stream<TopicTag> findAllRelateTopic(long topicId, int size) {
		return entityManager.createQuery("SELECT tt FROM TopicTag tt WHERE tt.status = ?1 AND EXISTS(SELECT 1 FROM TopicTag t1 WHERE t1.topicId = ?2 AND t1.status = ?3 AND t1.names = tt.names)", TopicTag.class)
				.setParameter(1, true)
				.setParameter(2, topicId)
				.setParameter(3, true)
				.setMaxResults(size)
				.getResultStream();
	}

	@Override
	public Stream<TopicTag> findAllRelateTopic(long topicId, Collection<String> tages) {
		if(null == tages || tages.isEmpty()){
			return Stream.empty();
		}
		//多少结果集
		int maxSize = tages.size() * 10;
		Optional<String> param = Commons.quoteSQLParameters(tages);
		if(!param.isPresent()){
			return Stream.empty();
		}
		return entityManager.createQuery("SELECT tt FROM TopicTag tt WHERE tt.topicId != ?1 AND tt.status = ?2 AND tt.names IN("+param.get()+")", TopicTag.class)
				.setParameter(1, topicId)
				.setParameter(2, true)
				.setMaxResults(maxSize)
				.getResultStream();
	}

	@Override
	public Map<String, Long> groupTagForNames(int size) {
		final String SQL="SELECT tt.names, COUNT(tt) FROM TopicTag tt GROUP BY tt.names ORDER BY COUNT(tt) DESC";
		@SuppressWarnings("unchecked")
		List<Object[]> result = entityManager.createQuery(SQL).setMaxResults(size).getResultList();
		//
		Map<String, Long> data = new HashMap<>();
		for(Object[] columns : result){
			String names = null;
			try{
				names = (String)columns[0];
			}catch(java.lang.ClassCastException e){}
			Long v = 0L;
			try{
				v = ((BigDecimal)columns[1]).longValue();
			}catch(java.lang.ClassCastException e){
				v = (Long)columns[1];
			}
			if(names==null){
				continue;
			}
			if(v == null){
				continue;
			}
			data.put(names, v);
			
		}
		return data;
	}
}
