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

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.BiFunction;
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.letterbox.api.dao.OutboxDao;
import com.apobates.forum.letterbox.api.service.OutboxService;
import com.apobates.forum.letterbox.entity.ForumLetter;
import com.apobates.forum.letterbox.entity.ForumLetterReceiver;
import com.apobates.forum.letterbox.entity.ForumLetterTypeEnum;
import com.apobates.forum.letterbox.entity.Outbox;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;

@Service
public class OutboxServiceImple implements OutboxService{
	@Autowired
	private OutboxDao outboxDao;
	
	@Override
	public Page<ForumLetter> getSent(long memberId, Pageable pageable) {
		Page<ForumLetter> rs = outboxDao.findAllBySender(memberId, pageable);
		
		final Stream<ForumLetter> result = associationLetterReceiversAsync(rs.getResult().collect(Collectors.toList()));
		return new Page<ForumLetter>(){
			@Override
			public long getTotalElements() {
				return rs.getTotalElements();
			}
			@Override
			public Stream<ForumLetter> getResult() {
				return result;
			}
			
		};
	}

	@Override
	public Page<ForumLetter> getSent(long memberId, ForumLetterTypeEnum label, Pageable pageable) {
		Page<ForumLetter> rs = outboxDao.findAllBySenderAndType(memberId, label, pageable);
		
		final Stream<ForumLetter> result = associationLetterReceiversAsync(rs.getResult().collect(Collectors.toList()));
		return new Page<ForumLetter>(){
			@Override
			public long getTotalElements() {
				return rs.getTotalElements();
			}
			@Override
			public Stream<ForumLetter> getResult() {
				return result;
			}
			
		};
	}
	
	@Override
	public long countSentByMember(long memberId) {
		return outboxDao.countBySender(memberId);
	}

	@Override
	public long countSentByMember(long memberId, ForumLetterTypeEnum label) {
		return outboxDao.countBySenderAndType(memberId, label);
	}

	// [异步]级联加载收件人
	private Stream<ForumLetter> associationLetterReceiversAsync(final List<ForumLetter> data) {
		if (null == data || data.isEmpty()) {
			return Stream.empty();
		}
		BiFunction<ForumLetter, List<Outbox>, ForumLetter> bi = (flr, outboxs) -> {
			Optional<ForumLetter> opt = Optional.ofNullable(flr);
			opt.ifPresent(fl -> {
				try {
					Set<ForumLetterReceiver> trs = outboxs.stream()
							.map(ob -> new ForumLetterReceiver(ob.getReceiver(), ob.getReceiverNickname()))
							.collect(Collectors.toSet());
					fl.setReceivers(trs);
				} catch (NullPointerException e) {
					fl.getReceivers().add(ForumLetterReceiver.allMemberes());
				}
			});
			return opt.orElse(null);
		};
		final Map<Long, List<Outbox>> boxes = CompletableFuture.supplyAsync(() -> data.stream().map(ForumLetter::getId).collect(Collectors.toSet()))
				.thenCompose(letterIdSet -> CompletableFuture.supplyAsync(() -> outboxDao.findAllByLetter(letterIdSet).collect(Collectors.groupingBy(Outbox::getLetter))))
				.join();
		return data.parallelStream().peek(flr -> bi.apply(flr, boxes.get(flr.getId()))).filter(Objects::nonNull);
	}
}
