package org.mercury_im.messenger.core.store.message;

import org.jivesoftware.smack.chat2.IncomingChatMessageListener;
import org.jivesoftware.smack.chat2.OutgoingChatMessageListener;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smackx.delay.packet.DelayInformation;
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
import org.jivesoftware.smackx.ox.OpenPgpContact;
import org.jivesoftware.smackx.ox.element.SigncryptElement;
import org.jivesoftware.smackx.ox_im.OxMessageListener;
import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.data.repository.DirectChatRepository;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.core.data.repository.PeerRepository;
import org.mercury_im.messenger.entity.Account;
import org.mercury_im.messenger.entity.message.Message;
import org.mercury_im.messenger.entity.message.MessageDirection;
import org.pgpainless.decryption_verification.OpenPgpMetadata;

import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.inject.Inject;
import javax.inject.Singleton;

import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;

@Singleton
public class MercuryMessageStore implements IncomingChatMessageListener, OxMessageListener {

    private static final Logger LOGGER = Logger.getLogger(MercuryMessageStore.class.getName());

    private final MessageRepository messageRepository;
    private final DirectChatRepository directChatRepository;
    private final PeerRepository peerRepository;
    private final SchedulersFacade schedulers;

    private Account account;

    private CompositeDisposable disposable = new CompositeDisposable();

    @Inject
    public MercuryMessageStore(Account account,
                               PeerRepository peerRepository,
                               DirectChatRepository directChatRepository,
                               MessageRepository messageRepository,
                               SchedulersFacade schedulers) {
        this.account = account;
        this.peerRepository = peerRepository;
        this.directChatRepository = directChatRepository;
        this.messageRepository = messageRepository;
        this.schedulers = schedulers;
    }

    @Override
    public void newIncomingMessage(EntityBareJid from,
                                   org.jivesoftware.smack.packet.Message smackMessage,
                                   org.jivesoftware.smack.chat2.Chat smackChat) {
        if (ExplicitMessageEncryptionElement.from(smackMessage) != null) {
            return;
        }
        Message message = new Message();
        message.setDirection(MessageDirection.incoming);
        DelayInformation delayInformation = DelayInformation.from(smackMessage);
        message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date());
        message.setSender(from);
        message.setRecipient(smackMessage.getTo().asEntityJidOrThrow());
        message.setXml(smackMessage.toXML().toString());
        if (smackMessage.getBody() != null) {
            message.setBody(smackMessage.getBody());
        }
        disposable.add(writeMessageToStore(from, message));
    }

    private Disposable writeMessageToStore(EntityBareJid peer, Message message) {
        return peerRepository.getOrCreatePeer(account, peer)
                .flatMap(directChatRepository::getOrCreateChatWithPeer)
                .map(chat -> {
                    message.setChatId(chat.getId());
                    return chat;
                })
                .flatMap(chat -> messageRepository.insertMessage(message))
                .subscribeOn(schedulers.getIoScheduler())
                .subscribe(m -> LOGGER.log(Level.INFO, "Message written"), e -> LOGGER.log(Level.SEVERE, "Error: ", e));
    }

    @Override
    public void newIncomingOxMessage(OpenPgpContact contact,
                                     org.jivesoftware.smack.packet.Message smackMessage,
                                     SigncryptElement decryptedPayload,
                                     OpenPgpMetadata metadata) {
        Message message = new Message();
        message.setDirection(MessageDirection.incoming);
        DelayInformation delayInformation = DelayInformation.from(smackMessage);
        message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date());
        message.setSender(contact.getJid().asEntityJidOrThrow());
        message.setRecipient(smackMessage.getTo().asEntityJidOrThrow());
        message.setXml(smackMessage.toXML().toString());
        org.jivesoftware.smack.packet.Message.Body body = decryptedPayload.getExtension(org.jivesoftware.smack.packet.Message.Body.ELEMENT,
                org.jivesoftware.smack.packet.Message.Body.NAMESPACE);
        if (body != null) {
            message.setBody(body.getMessage());
        }
        disposable.add(writeMessageToStore(contact.getJid().asEntityBareJidOrThrow(), message));
    }
}
