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

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
import java.util.stream.Stream;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
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.event.elderly.ForumActionEnum;
import com.apobates.forum.member.api.dao.MemberActiveRecordsDao;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.entity.MemberActiveRecords;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Repository
public class MemberActiveRecordsDaoImpl implements MemberActiveRecordsDao{
	@PersistenceContext
	private EntityManager entityManager;
	private final static Logger logger = LoggerFactory.getLogger(MemberActiveRecordsDaoImpl.class);

	@Transactional(propagation = Propagation.REQUIRED)
	@Override
	public void save(MemberActiveRecords entity) {
		entityManager.persist(entity);
	}

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

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

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

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

	@Override
	public Stream<MemberActiveRecords> findAllByMember(long memberId, String memberNames, int showSize) {
		return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.action = ?2 ORDER BY mar.id DESC", MemberActiveRecords.class)
				.setParameter(1, memberNames)
				.setParameter(2, ForumActionEnum.MEMBER_LOGIN)
				.setMaxResults(showSize)
				.getResultStream();
	}
	
	@Override
	public Stream<MemberActiveRecords> findAllLoginAction(long memberId, String memberNames, int showSize) {
		return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.action = ?2 AND mar.succeed = ?3 ORDER BY mar.id DESC", MemberActiveRecords.class)
				.setParameter(1, memberNames)
				.setParameter(2, ForumActionEnum.MEMBER_LOGIN)
				.setParameter(3, true)
				.setMaxResults(showSize)
				.getResultStream();
	}

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

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

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

	private long findAllCount(LocalDateTime start, LocalDateTime finish){
		try {
			return entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar WHERE mar.activeDateTime BETWEEN ?1 AND ?2", Long.class)
					.setParameter(1, start)
					.setParameter(2, finish)
					.getSingleResult();
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("[findAllCount][MemberActiveRecordsDao]", e);
			}
		}
		return 0L;
	}
	@Override
	public Page<MemberActiveRecords> findAll(Pageable pageable) {
		final long total = count();
		if (total == 0) {
			return emptyResult();
		}
		TypedQuery<MemberActiveRecords> query = entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar ORDER BY mar.activeDateTime DESC", MemberActiveRecords.class);
		query.setFirstResult(pageable.getOffset());
		query.setMaxResults(pageable.getPageSize());

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

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

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

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

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

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

	@Override
	public Stream<MemberActiveRecords> findAllByMemberNames(String memberNames, int showSize) {
		return entityManager.createQuery("SELECT mar FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND "
																						+ "mar.action = ?2 AND "
																						+ "mar.succeed = ?3 AND "
																						+ "mar.city IS NOT NULL AND "
																						+ "mar.province IS NOT NULL "
																						+ "ORDER BY mar.id DESC", MemberActiveRecords.class)
				.setParameter(1, memberNames)
				.setParameter(2, ForumActionEnum.MEMBER_LOGIN)
				.setParameter(3, true)
				.setMaxResults(showSize)
				.getResultStream();
	}

	@Override
	public EnumMap<ForumActionEnum, Long> statsMemberAction(long memberId) {
		Member m = entityManager.find(Member.class, memberId);
		EnumMap<ForumActionEnum, Long> data = new EnumMap<>(ForumActionEnum.class);
		if(null == m){
			return data;
		}
		// 就是统计登录次数
		Long count = 0L;
		try{
			count = entityManager.createQuery("SELECT COUNT(mar) FROM MemberActiveRecords mar WHERE mar.memberNames = ?1 AND mar.succeed = ?2 AND mar.action = ?3", Long.class)
				.setParameter(1, m.getNames())
				.setParameter(2, true)
				.setParameter(3, ForumActionEnum.MEMBER_LOGIN)
				.getSingleResult();
		}catch(Exception e){
			if (logger.isDebugEnabled()) {
				logger.debug("[statsMemberTopicAction][MemberActiveRecordsDao]", e);
			}
		}
		
		data.put(ForumActionEnum.MEMBER_REGISTER, 1L);
		data.put(ForumActionEnum.MEMBER_LOGIN, count);
		return data;
	}

	@Override
	public EnumMap<ForumActionEnum, Long> statsMemberAction(long memberId, List<ForumActionEnum> actions) {
		EnumMap<ForumActionEnum, Long> data = new EnumMap<>(ForumActionEnum.class);
		if(null == actions || actions.isEmpty()){
			return data;
		}
		List<ForumActionEnum> params = new ArrayList<>();
		for(ForumActionEnum fae : actions){
			if(ForumActionEnum.MEMBER_REGISTER != fae && ForumActionEnum.MEMBER_REGISTER != fae){
				params.add(fae);
			}
		}
		if(params.isEmpty()){
			return data;
		}
		@SuppressWarnings("unchecked")
		List<Object[]> result = entityManager.createQuery("SELECT mar.action, COUNT(mar) FROM MemberActiveRecords mar WHERE mar.memberId = ?1 AND mar.succeed = ?2 AND mar.action IN ?3 GROUP BY mar.action")
				.setParameter(1, memberId)
				.setParameter(2, true)
				.setParameter(3, params)
				.getResultList();
		data.putAll(groupMemberActionResultMapping(result));
		return data;
	}
	
	private EnumMap<ForumActionEnum, Long> groupMemberActionResultMapping(List<Object[]> result){
		EnumMap<ForumActionEnum, Long> data = new EnumMap<>(ForumActionEnum.class);
		if (result == null || result.isEmpty()) {
			return data;
		}
		for (Object[] objArr : result) {
			ForumActionEnum action = null;
			if (objArr[0] instanceof ForumActionEnum) {
				action = (ForumActionEnum) objArr[0];
			}
			if (action == null) {
				continue;
			}
			Long size = 0L;
			try{
				size = ((BigDecimal)objArr[1]).longValue();
			}catch(ClassCastException e){
				size = (Long) objArr[1];
			}
			data.put(action, size);
		}
		return data;
	}

	@Override
	public Map<String, Long> groupForDevice() {
		Query query = entityManager.createQuery("SELECT mar.device, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.device");
		@SuppressWarnings("unchecked")
		List<Object[]> resultList = query.getResultList();
		return toGroupResultMap(resultList);
	}

	@Override
	public Map<String, Long> groupForISP() {
		Query query = entityManager.createQuery("SELECT mar.isp, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.isp");
		@SuppressWarnings("unchecked")
		List<Object[]> resultList = query.getResultList();
		return toGroupResultMap(resultList);
	}

	@Override
	public Map<String, Long> groupForProvince() {
		Query query = entityManager.createQuery("SELECT mar.province, COUNT(mar) FROM MemberActiveRecords mar GROUP BY mar.province");
		@SuppressWarnings("unchecked")
		List<Object[]> resultList = query.getResultList();
		return toGroupResultMap(resultList);
	}
	private Map<String, Long> toGroupResultMap(List<Object[]> result){
		Map<String, Long> data = new HashMap<>();
		if (result == null || result.isEmpty()) {
			return data;
		}
		for (Object[] objArr : result) {
			String attrVal = null;
			if (objArr[0] instanceof String) {
				attrVal = objArr[0].toString();
			}
			if (attrVal == null) {
				continue;
			}
			Long size = 0L;
			try{
				size = ((BigDecimal)objArr[1]).longValue();
			}catch(ClassCastException e){
				size = (Long) objArr[1];
			}
			data.put(attrVal, size);
		}
		return data;
	}
	@Override
	public TreeMap<String, Long> groupMemberForActivity(LocalDateTime start, LocalDateTime finish) {
		final String SQL="SELECT FUNCTION('DATE', mar.activeDateTime), COUNT(mar) FROM MemberActiveRecords mar WHERE mar.activeDateTime BETWEEN ?1 AND ?2 GROUP BY FUNCTION('DATE', mar.activeDateTime)";
		@SuppressWarnings("unchecked")
		List<Object[]> result = entityManager.createQuery(SQL).setParameter(1, start).setParameter(2, finish).getResultList();
		//
		TreeMap<String, Long> data = new TreeMap<>();
		for(Object[] columns : result){
			String k = null;
			try{
				k = (String)columns[0];
			}catch(java.lang.ClassCastException e){
				k = columns[0].toString();
			}
			Long v = 0L;
			try{
				v = ((BigDecimal)columns[1]).longValue();
			}catch(java.lang.ClassCastException e){
				v = (Long)columns[1];
			}
			if(k==null || v == null){
				continue;
			}
			data.put(k, v);
		}
		return data;
	}
}
