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

import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.apobates.forum.core.api.TopicStatsCollect;
import com.apobates.forum.core.api.TopicStatsCollect.TopicStatsCollectBuilder;
import com.apobates.forum.core.api.dao.PostsDao;
import com.apobates.forum.core.api.dao.TopicActionCollectionDao;
import com.apobates.forum.core.api.dao.TopicDao;
import com.apobates.forum.core.api.service.TopicActionCollectionService;
import com.apobates.forum.core.entity.Posts;
import com.apobates.forum.core.entity.Topic;
import com.apobates.forum.core.entity.TopicActionCollection;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.member.api.dao.ForumScoreRoleDao;
import com.apobates.forum.member.entity.ForumScoreRole;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Service
public class TopicActionCollectionServiceImpl implements TopicActionCollectionService{
	@Autowired
	private TopicActionCollectionDao topicActionCollectionDao;
	@Autowired
	private ForumScoreRoleDao forumScoreRoleDao;
	@Autowired
	private PostsDao postsDao;
	@Autowired
	private TopicDao topicDao;
	
	@Override
	public Stream<TopicActionCollection> getRecentByMember(long memberId, int size) {
		return topicActionCollectionDao.findRecentByMember(memberId, size);
	}

	@Override
	public Page<TopicActionCollection> getByMember(long memberId, Pageable pageable) {
		Page<TopicActionCollection> rs = topicActionCollectionDao.findByMember(memberId, pageable);
		Stream<TopicActionCollection> result = associationTopicStats(rs.getResult());
		
		return new Page<TopicActionCollection>(){
			@Override
			public long getTotalElements() {
				return rs.getTotalElements();
			}
			@Override
			public Stream<TopicActionCollection> getResult() {
				return result;
			}
		};
	}
	
	@Override
	public long countByMember(long memberId) {
		return topicActionCollectionDao.findByMemberCount(memberId);
	}

	@Override
	public EnumMap<ForumActionEnum, Long> statsMemberTopicAction(long memberId) {
		if(memberId > 0){
			return topicActionCollectionDao.groupMemberAction(memberId, Arrays.asList(ForumActionEnum.TOPIC_PUBLISH, ForumActionEnum.POSTS_REPLY));
		}
		return new EnumMap<>(ForumActionEnum.class);
	}

	@Override
	public EnumMap<ForumActionEnum, Long> statsMemberAction(long memberId, List<ForumActionEnum> actions) {
		if(memberId > 0){
			return topicActionCollectionDao.groupMemberAction(memberId, actions);
		}
		return new EnumMap<>(ForumActionEnum.class);
	}

	@Override
	public EnumMap<ForumActionEnum, Long> groupMemberTopicAction(long memberId) {
		if(memberId > 0){
			return topicActionCollectionDao.groupMemberAction(memberId, allowActionSet());
		}
		return new EnumMap<>(ForumActionEnum.class);
	}

	@Override
	public Page<TopicActionCollection> getAll(Pageable pageable) {
		return topicActionCollectionDao.findAll(pageable);
	}

	@Override
	public Map<Long, EnumMap<ForumActionEnum, Long>> groupMemberTopicAction(List<Long> memberIdList) {
		if(memberIdList == null || memberIdList.isEmpty()){
			return Collections.emptyMap();
		}
		return topicActionCollectionDao.groupMemberAction(memberIdList, allowActionSet());
	}

	@Override
	public EnumMap<ForumActionEnum, Long> sumTopicAndPosts() {
		return topicActionCollectionDao.groupAction(Arrays.asList( ForumActionEnum.TOPIC_PUBLISH, ForumActionEnum.POSTS_REPLY));
	}

	@Override
	public Map<Long, Map<String, Long>> orderMemberTopices(int size) {
		return topicActionCollectionDao.groupMemberAction(ForumActionEnum.TOPIC_PUBLISH, size);
	}

	@Override
	public Page<TopicActionCollection> getAllByMemberAction(long memberId, ForumActionEnum action, Pageable pageable) {
		Page<TopicActionCollection> rs = topicActionCollectionDao.findAllByActionAndMember(action, memberId, pageable);
		Stream<TopicActionCollection> result = associationTopicStats(rs.getResult());
		
		return new Page<TopicActionCollection>(){
			@Override
			public long getTotalElements() {
				return rs.getTotalElements();
			}
			@Override
			public Stream<TopicActionCollection> getResult() {
				return result;
			}
		};
	}

	@Override
	public long countAllByMemberAction(long memberId, ForumActionEnum action) {
		return topicActionCollectionDao.findAllByActionAndMemberCount(action, memberId);
	}

	@Override
	public Page<TopicActionCollection> getByTopic(long topicId, Pageable pageable) {
		return topicActionCollectionDao.findByTopic(topicId, pageable);
	}

	@Override
	public Page<TopicActionCollection> getAllByMemberAction(long memberId, List<ForumActionEnum> actiones, Pageable pageable) {
		return topicActionCollectionDao.findAllByActionAndMember(actiones, memberId, pageable);
	}

	@Override
	public Page<TopicActionCollection> getRecycleTopic(long memberId, Pageable pageable) {
		Page<TopicActionCollection> data = getAllByMemberAction(memberId, Arrays.asList(ForumActionEnum.TOPIC_DEL, ForumActionEnum.POSTS_DEL), pageable);
		Map<Long, TopicActionCollection> tacMap = data.getResult().collect(Collectors.toMap(TopicActionCollection::getPostsId, Function.identity()));
		if (null == tacMap || tacMap.isEmpty()) {
			return Page.empty();
		}
		Map<Long, Posts> postsMap = postsDao.findAll(tacMap.keySet()).collect(Collectors.toMap(Posts::getId, Function.identity()));
		BiFunction<Posts, TopicActionCollection, TopicActionCollection> mapper = (p, tc) -> {
			Optional<TopicActionCollection> taco = Optional.ofNullable(tc);
			taco.ifPresent(tmp -> tmp.setPosts(p));
			return taco.orElse(null);
		};
		Stream<TopicActionCollection> rs = tacMap.keySet().stream().mapToLong(Long::valueOf).mapToObj((postsId) -> mapper.apply(postsMap.get(postsId), tacMap.get(postsId))).filter(Objects::nonNull);
		return new Page<TopicActionCollection>() {
			@Override
			public long getTotalElements() {
				return data.getTotalElements();
			}

			@Override
			public Stream<TopicActionCollection> getResult() {
				return rs;
			}
		};
	}
	
	@Override
	public TopicStatsCollect getStats(long memberId) {
		TopicStatsCollectBuilder tscb = new TopicStatsCollectBuilder(memberId);
		Map<ForumActionEnum,Long> statsActions = statsMemberAction(memberId, Arrays.asList(ForumActionEnum.TOPIC_PUBLISH, ForumActionEnum.POSTS_REPLY, ForumActionEnum.TOPIC_LIKED, ForumActionEnum.TOPIC_FAVORITE));
		try{
			Long ts = statsActions.get(ForumActionEnum.TOPIC_PUBLISH);
			tscb.threads(Commons.optionalOrZero(ts));
		}catch(Exception e){}
		try{
			Long rs = statsActions.get(ForumActionEnum.POSTS_REPLY);
			tscb.replies(Commons.optionalOrZero(rs));
		}catch(Exception e){}
		//
		try{
			Long tls = statsActions.get(ForumActionEnum.TOPIC_LIKED);
			tscb.threadsLikes(Commons.optionalOrZero(tls));
		}catch(Exception e){}
		try{
			Long fls = statsActions.get(ForumActionEnum.TOPIC_FAVORITE);
			tscb.threadsFavorites(Commons.optionalOrZero(fls));
		}catch(Exception e){}
		return tscb.build();
	}

	/**
	 * 
	 * @param queryResult 未加载Topic的话题操作集合
	 * @return 返回加载Topic完的话题操作集合
	 */
	private Stream<TopicActionCollection> associationTopicStats(Stream<TopicActionCollection> queryResult) {
		/* 不要使用Map<Key,Entity>模式 */
		List<TopicActionCollection> rs = queryResult.collect(Collectors.toList());
		if (null == rs || rs.isEmpty()) {
			return Stream.empty();
		}
		Set<Long> topicIdSet = rs.stream().map(TopicActionCollection::getTopicId).collect(Collectors.toSet());
		Map<Long, Topic> topicCollect = topicDao.findAllById(topicIdSet).stream().collect(Collectors.toMap(Topic::getId, Function.identity()));
		//
		BiFunction<TopicActionCollection, Topic, TopicActionCollection> bi = (tac, t) -> {
			Optional<TopicActionCollection> taco = Optional.ofNullable(tac);
			taco.ifPresent(tmp -> tmp.setTopic(t));
			return taco.orElse(null);
		};
		return rs.stream().map(tac -> bi.apply(tac, topicCollect.get(tac.getTopicId()))).filter(Objects::nonNull);
	}
	//取消原来的固有动作,而是查看可用的积分规则
	private Set<ForumActionEnum> allowActionSet(){
		return forumScoreRoleDao.findAllUsed().map(ForumScoreRole::getAction).collect(Collectors.toSet());
	}
}
