package org.mercury_im.messenger.core.viewmodel.chat;

import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.core.Messenger;
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.core.util.Optional;
import org.mercury_im.messenger.core.viewmodel.MercuryViewModel;
import org.mercury_im.messenger.entity.chat.DirectChat;
import org.mercury_im.messenger.entity.contact.Peer;
import org.mercury_im.messenger.entity.message.Message;

import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.functions.Function;
import io.reactivex.subjects.BehaviorSubject;
import lombok.Getter;

public class ChatViewModel implements MercuryViewModel {

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

    private final Messenger messenger;
    private final PeerRepository contactRepository;
    private final DirectChatRepository directChatRepository;
    private final MessageRepository messageRepository;
    private final SchedulersFacade schedulers;

    @Getter
    private BehaviorSubject<Optional<Peer>> peer = BehaviorSubject.create();

    @Getter
    private BehaviorSubject<DirectChat> chat = BehaviorSubject.create();

    @Getter
    private Observable<List<Message>> messages;

    @Getter
    private Observable<String> contactDisplayName;

    private final BehaviorSubject<Observable<List<Message>>> messageQueryObservable = BehaviorSubject.create();


    public ChatViewModel(Messenger messenger,
                         PeerRepository peerRepository,
                         DirectChatRepository directChatRepository,
                         MessageRepository messageRepository,
                         SchedulersFacade schedulers) {
        this.messenger = messenger;
        this.contactRepository = peerRepository;
        this.directChatRepository = directChatRepository;
        this.messageRepository = messageRepository;
        this.schedulers = schedulers;
    }

    public void init(UUID accountId, EntityBareJid contactJid) {
        contactRepository.getOrCreatePeer(accountId, contactJid)
                .flatMapObservable(contactRepository::observePeer)
                .subscribe(peer);

        peer.compose(schedulers.executeUiSafeObservable())
                .subscribe();

        peer.filter(Optional::isPresent).map(Optional::getItem)
                .flatMap(p -> directChatRepository.getOrCreateChatWithPeer(p).toObservable())
                .subscribe(chat);

        chat.compose(schedulers.executeUiSafeObservable()).subscribe();

        Observable<List<Message>> allMessagesObservable = chat.flatMap(messageRepository::observeMessages);
        messageQueryObservable.onNext(allMessagesObservable);
        messages = Observable.switchOnNext(messageQueryObservable);
        contactDisplayName = peer.filter(Optional::isPresent).map(Optional::getItem)
                .map(Peer::getDisplayName);
    }

    public void onQueryTextChanged(String query) {
        if (query.trim().isEmpty()) {
            messageQueryObservable.onNext(chat.flatMap(messageRepository::observeMessages));
        } else {
            messageQueryObservable.onNext(chat.flatMap(c -> messageRepository.findMessagesWithBody(c, query)));
        }
    }

    public void deleteContact() {
        addDisposable(peer.single(new Optional<>())
                .filter(Optional::isPresent)
                .map(Optional::getItem)
                .flatMapCompletable(messenger::deleteContact)
                .compose(schedulers.executeUiSafeCompletable())
                .subscribe(
                        () -> LOGGER.log(Level.INFO, "Contact deleted."),
                        e -> LOGGER.log(Level.SEVERE, "Error deleting contact.", e)));
    }

    public void sendMessage(String body) {
        addDisposable(messenger.getMessageConsignor(chat.getValue())
                .sendTextMessage(chat.getValue(), body)
                .compose(schedulers.executeUiSafeSingle())
                .subscribe(messageId -> LOGGER.log(Level.INFO, "Successfully sent message."),
                        e -> LOGGER.log(Level.WARNING, "Error sending message.", e)));
    }
}
