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

import com.apobates.forum.core.dao.PostsDao;
import com.apobates.forum.core.dao.TopicActionCollectionDao;
import com.apobates.forum.core.dao.TopicDao;
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.core.entity.proxy.TopicActionCollectionReplica;
import com.apobates.forum.core.service.TopicActionCollectionService;
import com.apobates.forum.event.elderly.ForumActionEnum;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;
import java.util.Arrays;
import java.util.Collections;
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;

/**
 *
 * @author xiaofanku
 * @since 20200514
 */
@Service
public class TopicActionCollectionServiceImpl implements TopicActionCollectionService{
    @Autowired
    private TopicActionCollectionDao topicActionCollectionDao;
    @Autowired
    private PostsDao postsDao;
    @Autowired
    private TopicDao topicDao;
    
    @Override
    public Page<TopicActionCollectionReplica> getRecycleTopic(long memberId, Pageable pageable) { //20200709
        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, TopicActionCollectionReplica> mapper = (p, tc)->{
            TopicActionCollectionReplica tacr = Optional.ofNullable(tc).map(TopicActionCollectionReplica.copyTopicAction).orElse(null);
            tacr.setPosts(p);
            return tacr;
        };
        Stream<TopicActionCollectionReplica> rs = tacMap.keySet().stream().mapToLong(Long::valueOf).mapToObj((postsId)->mapper.apply(postsMap.get(postsId), tacMap.get(postsId))).filter(Objects::nonNull);
        
        return new Page<TopicActionCollectionReplica>() {
            @Override
            public long getTotalElements() {
                return data.getTotalElements();
            }
            
            @Override
            public Stream<TopicActionCollectionReplica> getResult() {
                return rs;
            }
        };
    }
    
    @Override
    public Page<TopicActionCollectionReplica> getByMember(long memberId, Pageable pageable) {
        Page<TopicActionCollection> rs = topicActionCollectionDao.findByMember(memberId, pageable);
        Stream<TopicActionCollectionReplica> result = associationTopicStats(rs.getResult());
        
        return new Page<TopicActionCollectionReplica>() {
            @Override
            public long getTotalElements() {
                return rs.getTotalElements();
            }
            
            @Override
            public Stream<TopicActionCollectionReplica> getResult() {
                return result;
            }
        };
    }
    
    @Override
    public long countByMember(long memberId) {
        return topicActionCollectionDao.findByMemberCount(memberId);
    }
    
    @Override
    public Page<TopicActionCollection> getByTopic(long topicId, Pageable pageable) {
        return topicActionCollectionDao.findByTopic(topicId, pageable);
    }
    
    @Override
    public Page<TopicActionCollection> getAll(Pageable pageable) {
        return topicActionCollectionDao.findAll(pageable);
    }
    
    @Override
    public Page<TopicActionCollectionReplica> getAllByMemberAction(long memberId, ForumActionEnum action, Pageable pageable) {
        Page<TopicActionCollection> rs = topicActionCollectionDao.findAllByActionAndMember(action, memberId, pageable);
        Stream<TopicActionCollectionReplica> result = associationTopicStats(rs.getResult());
        
        return new Page<TopicActionCollectionReplica>() {
            @Override
            public long getTotalElements() {
                return rs.getTotalElements();
            }
            
            @Override
            public Stream<TopicActionCollectionReplica> getResult() {
                return result;
            }
        };
    }
    
    @Override
    public long countAllByMemberAction(long memberId, ForumActionEnum action) {
        return topicActionCollectionDao.findAllByActionAndMemberCount(action, memberId);
    }
    
    @Override
    public Page<TopicActionCollection> getAllByMemberAction(long memberId, List<ForumActionEnum> actiones, Pageable pageable) {
        //DAO会进行检查
        return topicActionCollectionDao.findAllByActionAndMember(actiones, memberId, pageable);
    }
    
    @Override
    public Stream<TopicActionCollection> getRecentByMember(long memberId, int size) {
        return topicActionCollectionDao.findRecentByMember(memberId, size);
    }
    
    @Override
    public Map<ForumActionEnum, Long> statsMemberTopicAction(long memberId) {
        if (memberId > 0) {
            return topicActionCollectionDao.groupMemberAction(memberId, Arrays.asList(ForumActionEnum.TOPIC_PUBLISH, ForumActionEnum.POSTS_REPLY));
        }
        return Collections.emptyMap();
    }
    
    @Override
    public Map<ForumActionEnum, Long> statsMemberAllAction(long memberId) {
        return topicActionCollectionDao.groupMemberAction(memberId);
    }
    
    
    @Override
    public Map<ForumActionEnum, Long> statsMemberAction(long memberId, List<ForumActionEnum> actions) {
        if (memberId > 0) {
            return topicActionCollectionDao.groupMemberAction(memberId, actions); //DAO会进行检查
        }
        return Collections.emptyMap();
    }
    
    @Override
    public Map<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);
    }
    
    /**
     *
     * @param queryResult 未加载Topic的话题操作集合
     * @return 返回加载Topic完的话题操作集合
     */
    private Stream<TopicActionCollectionReplica> associationTopicStats(Stream<TopicActionCollection> queryResult) {
        //一对多时可能出现异常:java.lang.IllegalStateException: Duplicate key com.apobates.forum.core.entity.TopicActionCollection@4a45bb7b
        /*不要使用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, TopicActionCollectionReplica> bi = (tac, t) -> {
            TopicActionCollectionReplica tacr = Optional.ofNullable(tac).map(TopicActionCollectionReplica.copyTopicAction).orElseGet(TopicActionCollectionReplica::new);
            tacr.setTopic(t);
            return tacr;
        };
        return rs.stream().map(tac->bi.apply(tac, topicCollect.get(tac.getTopicId())));
    }
}