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

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.apobates.forum.core.api.dao.BoardDao;
import com.apobates.forum.core.api.dao.BoardGroupDao;
import com.apobates.forum.core.api.dao.BoardModeratorDao;
import com.apobates.forum.core.api.dao.BoardModeratorRoleHistoryDao;
import com.apobates.forum.core.api.service.BoardModeratorService;
import com.apobates.forum.core.entity.Board;
import com.apobates.forum.core.entity.BoardGroup;
import com.apobates.forum.core.entity.BoardModerator;
import com.apobates.forum.core.entity.BoardModeratorRoleHistory;
import com.apobates.forum.core.entity.ModeratorLevelEnum;
import com.apobates.forum.core.impl.event.ForumEventPublisher;
import com.apobates.forum.core.impl.event.ModeratorBornEvent;
import com.apobates.forum.core.impl.event.ModeratorRecallEvent;
import com.apobates.forum.member.entity.Member;
import com.apobates.forum.member.entity.MemberRoleEnum;
import com.apobates.forum.utils.lang.TriFunction;

@Service
@CacheConfig
public class BoardModeratorServiceImpl implements BoardModeratorService{
	@Autowired
	private BoardModeratorDao boardModeratorDao;
	@Autowired
	private BoardModeratorRoleHistoryDao boardModeratorRoleHistoryDao;
	@Autowired
	private BoardDao boardDao;
	@Autowired
	private BoardGroupDao boardGroupDao;
	@Autowired
	private ForumEventPublisher forumEventPublisher;
	private final static Logger logger = LoggerFactory.getLogger(BoardModeratorServiceImpl.class);
	
	@Cacheable(value="boardCache", key = "'moderator_'+#boardId")
	@Override
	public List<BoardModerator> getAllUsedByBoardId(long boardId) { //[BC]B4
		if(boardId == 0){
			return Collections.emptyList();
		}
		return boardModeratorDao.findUsedByBoardId(boardId);
	}
	
	@Override
	public Optional<BoardModerator> get(int boardGroupId, long boardId, long memberId) {
		if(boardGroupId == 0 || boardId == 0 || memberId == 0){
			return Optional.empty();
		}
		Optional<BoardModerator> data = get(boardId, memberId);
		if(data.isPresent()){
			return data;
		}
		//不是版主?是大版主吗?
		return boardModeratorDao.findOneByBoardGroupAndMember(boardGroupId, memberId);
	}
	
	@Override
	public Stream<BoardModerator> getAllByBoardId(long boardId) {
		if(boardId == 0){ //可能大版主不显示
			return Stream.empty();
		}
		return boardModeratorDao.findAllByBoardId(boardId);
	}
	
	@Override
	public Stream<BoardModerator> getAllUsedByBoardGroupId(int boardGroupId) {
		if (0 >= boardGroupId) {
			return Stream.empty();
		}
		return boardModeratorDao.findAllUsedByBoardGroup(boardGroupId);
	}

	@Override
	public Stream<BoardModerator> getAllUsedByBoardId(final int boardGroupId, final long boardId) {
		final TriFunction<BoardGroup, Board, BoardModerator, BoardModerator> action = (bg, b, bm) -> {
			Optional<BoardModerator> opt = Optional.ofNullable(bm);
			if (null != b) {
				opt.ifPresent(tbm->tbm.setBoard(b));
			}
			if (null != bg && bg.getId() > 0) {
				opt.ifPresent(tbm->tbm.setVolumes(bg));
			}
			return opt.orElse(null);
		};
		CompletableFuture<List<BoardModerator>> bm = CompletableFuture.supplyAsync(() -> boardModeratorDao.findUsedByBoardId(boardId));
		CompletableFuture<Stream<BoardModerator>> gbm = CompletableFuture.supplyAsync(() -> boardModeratorDao.findAllUsedByBoardGroup(boardGroupId));
		return bm.thenCombine(gbm, (mb, gmb) -> Stream.concat(mb.stream(), gmb))
				.thenCompose(allMS -> CompletableFuture
						.supplyAsync(() -> boardDao.findOne(boardId).orElse(Board.empty(boardId))).thenCombine(
								CompletableFuture.supplyAsync(
										() -> boardGroupDao.findOne(boardGroupId).orElse(BoardGroup.empty())),
								(board, boardGroup) -> {
									return allMS.map(moderator -> action.apply(boardGroup, board, moderator)).filter(Objects::nonNull);
								}))
				.join();
	}

	@CacheEvict(value = "boardCache", key = "'moderator_'+#boardId", condition = "#boardId > 0")
	@Override
	public Optional<BoardModerator> create(int volumesId, long boardId, Member member, ModeratorLevelEnum level)throws IllegalArgumentException {
		if (0 == boardId) { // 若是大版主
			return create(volumesId, member, level);
		}
		if (null == member) {
			throw new IllegalArgumentException("会员不存在");
		}
		BoardModerator moderator = new BoardModerator(volumesId, boardId, level, member.getId(), member.getNickname());
		BoardModeratorRoleHistory memberRoleHistory = new BoardModeratorRoleHistory(-1L, member.getMrole(), MemberRoleEnum.BM, volumesId, boardId, member.getId()); // 需要回填版主的ID
		return storeReplicaModerator(moderator, member, memberRoleHistory);
	}

	@Override
	public Optional<BoardModerator> create(int volumesId, Member member, ModeratorLevelEnum level)throws IllegalArgumentException {
		if (null == member) {
			throw new IllegalArgumentException("会员不存在");
		}
		BoardModerator moderator = new BoardModerator(volumesId, 0, level, member.getId(), member.getNickname(), true);
		BoardModeratorRoleHistory memberRoleHistory = new BoardModeratorRoleHistory(-1L, member.getMrole(), MemberRoleEnum.MASTER, volumesId, member.getId()); // 需要回填版主的ID
		return storeReplicaModerator(moderator, member, memberRoleHistory);
	}
	
	@Override
	public Optional<BoardModerator> get(long id) {
		if(id>0){
			return boardModeratorDao.findOne(id);
		}
		return Optional.empty();
	}
	
	@Override
	public Optional<Boolean> remove(int volumesId, long boardId, long memberId) throws IllegalStateException {
		List<BoardModeratorRoleHistory> his = boardModeratorRoleHistoryDao.findAllByMember(memberId).collect(Collectors.toList());
		// 20200616移除的角色变化记录
		BoardModeratorRoleHistory removeHis = null;
		// 其它的记录
		List<BoardModeratorRoleHistory> otherHis = new ArrayList<>();
		for (BoardModeratorRoleHistory bmr : his) {
			if (bmr.getBoardId() == boardId && bmr.getVolumesId() == volumesId && bmr.getMemberId() == memberId) {
				removeHis = bmr;
			} else {
				otherHis.add(bmr);
			}
		}
		if (null == removeHis) {
			throw new IllegalStateException("角色变化记录不存在");
		}
		// 会员更新的角色
		MemberRoleEnum updateRole;
		if (otherHis.isEmpty()) {
			updateRole = MemberRoleEnum.NO;
		} else {
			updateRole = otherHis.stream().map(BoardModeratorRoleHistory::getInvestRole).max(Comparator.comparing(MemberRoleEnum::getSymbol)).get();
		}
		// 版主记录
		// 版主权限
		try {
			boardModeratorDao.deleteModerator(removeHis);
			forumEventPublisher.buildModeratorRecallEvent(new ModeratorRecallEvent(this, removeHis, updateRole));
			return Optional.of(true);
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("版主卸任失败", e);
			}
		}
		return Optional.empty();
	}
	
	@Override
	public Optional<Boolean> remove(int volumesId, long memberId) throws IllegalStateException{
		return remove(volumesId, 0, memberId);
	}

	@Override
	public Stream<BoardModerator> getAll() {
		List<BoardModerator> rs = boardModeratorDao.findAll().collect(Collectors.toList());
		if (null == rs || rs.isEmpty()) {
			return Stream.empty();
		}
		final Map<Integer,Set<Long>> allQueryParam = rs.stream().collect(Collectors.groupingBy(BoardModerator::getVolumesId, Collectors.mapping(BoardModerator::getBoardId, Collectors.toSet())));
		final TriFunction<BoardGroup, Board, BoardModerator, BoardModerator> action = (bg, b, bm) -> {
			Optional<BoardModerator> opt = Optional.ofNullable(bm);
			if (null != b) {
				opt.ifPresent(tbm->tbm.setBoard(b));
			}
			if (null != bg && bg.getId() > 0) {
				opt.ifPresent(tbm->tbm.setVolumes(bg));
			}
			return opt.orElse(null);
		};
		Set<Long> boardIds = allQueryParam.values().stream().flatMap(innerSet->innerSet.stream()).filter(boardId->boardId>0).collect(Collectors.toSet());
		CompletableFuture<Map<Long, Board>> boardMap = CompletableFuture.supplyAsync(() -> boardDao.findAllById(boardIds)).thenApply(boards -> boards.collect(Collectors.toMap(Board::getId, Function.identity())));
		CompletableFuture<Map<Integer, BoardGroup>> boardGroupMap = CompletableFuture.supplyAsync(() -> boardGroupDao.findAllById(allQueryParam.keySet())).thenApply(boardGroups -> boardGroups.collect(Collectors.toMap(BoardGroup::getId, Function.identity())));
		return boardMap.thenCombine(boardGroupMap, (bmap, bgmap) -> {
			return rs.parallelStream().map(ibm -> action.apply(bgmap.get(ibm.getVolumesId()), bmap.get(ibm.getBoardId()), ibm)).filter(Objects::nonNull);
		}).join();
	}
	
	@Override
	public Optional<Boolean> edit(long id, BoardModerator updateModerator)throws IllegalStateException {
		BoardModerator bm = get(id).orElseThrow(()->new IllegalStateException("版主不存在"));
		bm.setVolumesId(updateModerator.getVolumesId());
		//不能从小版主换成大版主
		if(bm.getBoardId()>0 && updateModerator.getBoardId()>0){
			bm.setBoardId(updateModerator.getBoardId());
		}
		bm.setMemberId(updateModerator.getMemberId());
		bm.setMemberNickname(updateModerator.getMemberNickname());
		bm.setStatus(updateModerator.isStatus());
		bm.setLevel(updateModerator.getLevel());
		return boardModeratorDao.edit(bm);
	}

	private Optional<BoardModerator> get(long boardId, long memberId) {
		if(boardId == 0 || memberId == 0){
			return Optional.empty();
		}
		return boardModeratorDao.findOneByBoardAndMember(boardId, memberId);
	}
	
	private Optional<BoardModerator> storeReplicaModerator(BoardModerator moderator, Member member, BoardModeratorRoleHistory memberRoleHistory) {
		try {
			Optional<BoardModerator> data = boardModeratorDao.pushModerator(moderator, memberRoleHistory);
			if (data.isPresent()) {
				forumEventPublisher.buildModeratorBornEvent(new ModeratorBornEvent(this, moderator, member, memberRoleHistory));
			}
			return data;
		} catch (Exception e) {
			if (logger.isDebugEnabled()) {
				logger.debug("版主创建失败", e);
			}
		}
		return Optional.empty();
	}
}
