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

import java.time.LocalDateTime;
import java.util.Collection;
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.BoardActionCollectionDao;
import com.apobates.forum.core.entity.BoardActionCollection;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

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

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

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

			@Override
			public Stream<BoardActionCollection> getResult() {
				return result;
			}
		};
	}
	
	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public void save(BoardActionCollection entity) {
		entityManager.persist(entity);
	}

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

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

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

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

	@Override
	public Stream<BoardActionCollection> findRecentByMember(long memberId, int size) {
		return entityManager
				.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.memberId = ?1 ORDER BY bac.entryDateTime DESC", BoardActionCollection.class)
				.setParameter(1, memberId)
				.setMaxResults(size)
				.getResultStream();
	}

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

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

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

	private long findByMemberCount(long memberId) {
		try {
			return entityManager
					.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.memberId = ?1", Long.class)
					.setParameter(1, memberId)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[findByMemberCount][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

	@Override
	public Page<BoardActionCollection> findAllByAction(ForumActionEnum action, Pageable pageable) {
		final long total = findAllByActionCount(action);
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<BoardActionCollection> query = entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.action = ?1 ORDER BY bac.entryDateTime DESC", BoardActionCollection.class).setParameter(1, action);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

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

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

	private long findAllByActionCount(ForumActionEnum action) {
		try {
			return entityManager
					.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.action = ?1", Long.class)
					.setParameter(1, action)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[findAllByActionCount][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

	@Override
	public Stream<ForumActionEnum> findAllAction() {
		return entityManager
				.createQuery("SELECT DISTINCT bac.action FROM BoardActionCollection bac", ForumActionEnum.class)
				.getResultStream();
	}

	@Override
	public Page<BoardActionCollection> findAll(LocalDateTime start, LocalDateTime finish, Pageable pageable) {
		final long total = findAllCount(start, finish);
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<BoardActionCollection> query = entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.entryDateTime BETWEEN ?1 AND ?2 ORDER BY bac.entryDateTime DESC", BoardActionCollection.class)
				.setParameter(1, start)
				.setParameter(2, finish);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

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

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

	private long findAllCount(LocalDateTime start, LocalDateTime finish) {
		try {
			return entityManager.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.entryDateTime BETWEEN ?1 AND ?2", Long.class)
					.setParameter(1, start)
					.setParameter(1, finish)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[findAllCount][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

	@Override
	public Stream<BoardActionCollection> findAllMemberFavoriteBoard(long memberId, int size) {
		return entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.action = ?1 AND bac.memberId = ?2", BoardActionCollection.class)
				.setParameter(1, ForumActionEnum.BOARD_FAVORITE)
				.setParameter(2, memberId)
				.setMaxResults(size)
				.getResultStream();
	}

	@Override
	public Optional<BoardActionCollection> findOneFavoriteRecord(long memberId, long boardId) {
		try{
			BoardActionCollection bac = entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.action = ?1 AND bac.memberId = ?2 AND bac.boardId = ?3", BoardActionCollection.class)
				.setParameter(1, ForumActionEnum.BOARD_FAVORITE)
				.setParameter(2, memberId)
				.setParameter(3, boardId)
				.getSingleResult();
			return Optional.ofNullable(bac);
		}catch(javax.persistence.NoResultException e){
			if(logger.isDebugEnabled()){
				logger.debug(e.getMessage(), e);
			}
		}
		return Optional.empty();
	}

	@Override
	public Stream<BoardActionCollection> findAllMemberAction(long memberId, long boardId, ForumActionEnum action) {
		return entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.action = ?1 AND bac.memberId = ?2 AND bac.boardId = ?3", BoardActionCollection.class)
				.setParameter(1, action)
				.setParameter(2, memberId)
				.setParameter(3, boardId)
				.getResultStream();
	}

	@Override
	public long countMemberAction(long memberId, long boardId, ForumActionEnum action) {
		logger.info("[BoarActiondDao][count]args: board id="+boardId+", member id="+memberId+", action="+action.name());
		try {
			return entityManager.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.action = ?1 AND bac.memberId = ?2 AND bac.boardId = ?3", Long.class)
					.setParameter(1, action)
					.setParameter(2, memberId)
					.setParameter(3, boardId)
					.getSingleResult();
		} catch (Exception e) {
			logger.info("[BoardActionDao][count]happed exception: "+e.getMessage());
			if (logger.isDebugEnabled()) {
				logger.debug("[countMemberAction][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

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

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

			@Override
			public Stream<BoardActionCollection> getResult() {
				return result;
			}
		};
	}
	private long countBoardAction(long boardId){
		try {
			return entityManager.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.boardId = ?1", Long.class)
					.setParameter(1, boardId)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[countBoardAction][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

	@Override
	public Page<BoardActionCollection> findAllByBoard(long boardId, ForumActionEnum action, Pageable pageable) {
		final long total = countBoardAction(boardId, action);
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<BoardActionCollection> query = entityManager.createQuery("SELECT bac FROM BoardActionCollection bac WHERE bac.boardId = ?1 AND bac.action = ?2 ORDER BY bac.entryDateTime DESC", BoardActionCollection.class)
				.setParameter(1, boardId)
				.setParameter(2, action);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

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

			@Override
			public Stream<BoardActionCollection> getResult() {
				return result;
			}
		};
	}
	
	private long countBoardAction(long boardId, ForumActionEnum action){
		try {
			return entityManager.createQuery("SELECT COUNT(bac) FROM BoardActionCollection bac WHERE bac.boardId = ?1 AND bac.action = ?2", Long.class)
					.setParameter(1, boardId)
					.setParameter(2, action)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[countBoardAction][BoardActionCollectionDao]", e);
			}
		}
		return 0L;
	}

	@Override
	public Optional<Boolean> isFavorited(long boardId, long memberId) throws IllegalStateException{
		logger.info("[BoardActionDao][Star]args: board id="+boardId+", member id="+memberId);
		if(boardId > 0 && memberId > 0){
			Long data = countMemberAction(memberId, boardId, ForumActionEnum.BOARD_FAVORITE);
			logger.info("[BoardActionDao][Star]result: size="+data);
			if(data!=null && data >0){
				logger.info("[BoardActionDao][Star]result: is exist");
				throw new IllegalStateException("收藏记录已经存在");
			}
			logger.info("[BoardActionDao][Star]result: not found");
			return Optional.of(true);
		}
		throw new IllegalStateException("非法的参数");
	}

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public int batchSave(Collection<BoardActionCollection> entities) {
		if (null == entities || entities.isEmpty()) {
			return 0;
		}
		int i = 0;
		for (BoardActionCollection bac : entities) {
			entityManager.persist(bac);
			i++;
			if (i % batchSize == 0) {
				// Flush a batch of inserts and release memory.
				entityManager.flush();
				entityManager.clear();
			}
		}
		return i;
	}
}
