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

import com.apobates.forum.attention.core.decorater.ForumEncoder;
import com.apobates.forum.letterbox.dao.ForumLetterDao;
import com.apobates.forum.letterbox.dao.OutboxDao;
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.letterbox.entity.proxy.ForumLetterReplica;
import com.apobates.forum.letterbox.impl.event.ForumLetterPostEvent;
import com.apobates.forum.letterbox.service.ForumLetterService;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.persistence.Page;
import com.apobates.forum.utils.persistence.Pageable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.github.davidmarquis.redisq.producer.MessageProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

/**
 *
 * @author xiaofanku
 * @since 20200511
 */
@Service
public class ForumLetterServiceImpl implements ForumLetterService{
    @Autowired
    private ForumLetterDao forumLetterDao;
    @Autowired
    private OutboxDao outboxDao;
    @Autowired @Qualifier("letterPostEventProducer")
    private MessageProducer<ForumLetterPostEvent> letterPostEventProducer;
    private final static Logger logger = LoggerFactory.getLogger(ForumLetterServiceImpl.class);

    @Override
    public long create(String title, String content, long receiver, String receiverNickname, long sender, String senderNickname) {
        return build(title, content, receiver, receiverNickname, sender, senderNickname).map(ForumLetterReplica::getId).get();
    }
    
    @Override
    public long create(ForumLetterTypeEnum label, String title, String content, long receiver, String receiverNickname, long sender, String senderNickname) {
        if (receiver <= 0) {
            return create(title, content, sender, senderNickname);
        }
        ForumLetter fl = new ForumLetter(title, Commons.htmlPurifier(content), sender, senderNickname, label);
        return create(fl, List.of(new ForumLetterReceiver(receiver, receiverNickname))).map(ForumLetterReplica::getId).get();
    }
    
    @Override
    public long create(String title, String content, long sender, String senderNickname) {
        ForumLetter fl = new ForumLetter(title, Commons.htmlPurifier(content), sender, senderNickname, ForumLetterTypeEnum.CLAIM);
        return create(fl, null).map(ForumLetterReplica::getId).get();
    }
    @Override
    public Optional<ForumLetterReplica> create(ForumLetter letter, Collection<ForumLetterReceiver> receiveres) {
        //无HTML tag,Emoji安全
        final String encodeLetterContent = new ForumEncoder(letter.getContent()).parseEmoji().getContent(); //.noneHtmlTag()
        final String encodeLetterTitle = new ForumEncoder(letter.getTitle()).noneHtmlTag().parseEmoji().getContent();
        letter.setContent(encodeLetterContent);
        letter.setTitle(encodeLetterTitle);
        
        try{
            ForumLetterReplica fr = forumLetterDao.pushLetter(letter, receiveres).orElseGet(ForumLetterReplica::new);
            if(fr.getId()>0){
                letterPostEventProducer.create(new ForumLetterPostEvent(fr.toEventPayload(), fr.getReceivers())).submit();
                return Optional.of(fr);
            }
        }catch(Exception e){
            if(logger.isDebugEnabled()){
                logger.debug("保存信件失败", e);
            }
        }
        return Optional.empty();
    }
    
    @Override
    public Optional<ForumLetterReplica> build(String title, String content, long receiver, String receiverNickname, long sender, String senderNickname) {
        ForumLetter fl = new ForumLetter(title, Commons.htmlPurifier(content), sender, senderNickname, ForumLetterTypeEnum.LETTER);
        return create(fl, List.of(new ForumLetterReceiver(receiver, receiverNickname)));
    }
    
    @Override
    public Optional<ForumLetterReplica> getOneLazyReceiver(final long id) {
        BiFunction<Optional<ForumLetter>, Set<ForumLetterReceiver>, Optional<ForumLetterReplica>> action = (ofl, frs)->{
            Optional<ForumLetterReplica> flro = ofl.map(ForumLetterReplica.copyLetter);
            flro.ifPresent(flr->flr.setReceivers(frs));
            return flro;
        };
        CompletableFuture<Optional<ForumLetter>> f1 = CompletableFuture.supplyAsync(()->forumLetterDao.findOne(id)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS);
        CompletableFuture<Set<ForumLetterReceiver>> res = CompletableFuture.supplyAsync(()->outboxDao.findAllByLetter(id)).thenApply((Stream<Outbox> outbox)->{
            return outbox.map(r->new ForumLetterReceiver(r.getReceiver(), r.getReceiverNickname())).collect(Collectors.toSet());
        }).completeOnTimeout(Collections.emptySet(), 1, TimeUnit.SECONDS);
        return f1.thenCombine(res, action).orTimeout(1, TimeUnit.SECONDS).join();
    }
    
    @Override
    public Page<ForumLetter> getMutualLetter(long sender, long receiver, Pageable pageable) {
        return forumLetterDao.findAll(sender, receiver, pageable);
    }
    
    @Override
    public Stream<ForumLetter> getMutualLetter(long sender, long receiver) {
        return forumLetterDao.findAll(sender, receiver);
    }
    
    @Override
    public Stream<ForumLetter> getTodayClaim() {
        return forumLetterDao.getTodayClaim();
    }
    
    @Override
    public Optional<ForumLetter> get(long id) {
        if(id>0){
            return forumLetterDao.findOne(id);
        }
        return Optional.empty();
    }
}