package org.mercury_im.messenger.android.ui.roster.contacts.detail;

import android.graphics.drawable.Drawable;
import android.util.Log;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.roster.PresenceEventListener;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smack.roster.RosterGroup;
import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.mercury_im.messenger.android.MercuryImApplication;
import org.mercury_im.messenger.core.Messenger;
import org.mercury_im.messenger.core.SchedulersFacade;
import org.mercury_im.messenger.core.data.repository.OpenPgpRepository;
import org.mercury_im.messenger.core.data.repository.PeerRepository;
import org.mercury_im.messenger.core.viewmodel.openpgp.FingerprintViewItem;
import org.mercury_im.messenger.entity.contact.Peer;
import org.mercury_im.messenger.android.ui.avatar.AvatarDrawable;
import org.mercury_im.messenger.core.util.CombinedPresenceListener;
import org.pgpainless.key.OpenPgpV4Fingerprint;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import javax.inject.Inject;

import io.reactivex.Completable;
import io.reactivex.disposables.CompositeDisposable;

public class ContactDetailViewModel extends ViewModel {

    @Inject
    PeerRepository peerRepository;

    @Inject
    OpenPgpRepository openPgpRepository;

    @Inject
    SchedulersFacade schedulers;

    @Inject
    Messenger messenger;

    private MutableLiveData<UUID> contactAccountId = new MutableLiveData<>(UUID.randomUUID());
    private MutableLiveData<String> contactAddress = new MutableLiveData<>("alice@wonderland.lit");
    private MutableLiveData<Drawable> contactAvatar = new MutableLiveData<>(new AvatarDrawable("Alice Wonderland", "alice@wonderland.lit"));
    private MutableLiveData<Presence.Mode> contactPresenceMode = new MutableLiveData<>(Presence.Mode.available);
    private MutableLiveData<String> contactPresenceStatus = new MutableLiveData<>("Going down the rabbit hole.");
    private MutableLiveData<String> contactName = new MutableLiveData<>("Alice Wonderland");
    private MutableLiveData<String> contactAccountAddress = new MutableLiveData<>("mad@hatter.lit");
    private MutableLiveData<List<String>> contactGroups = new MutableLiveData<>(Collections.emptyList());
    private MutableLiveData<List<FingerprintViewItem>> contactFingerprints = new MutableLiveData<>(Collections.emptyList());

    private Roster roster;

    private CompositeDisposable disposable = new CompositeDisposable();

    public ContactDetailViewModel() {
        super();
        MercuryImApplication.getApplication().getAppComponent().inject(this);
    }

    public void bind(UUID accountId, String peerAddress) {
        Log.d("MMMMMM", "Bind!");
        EntityBareJid peerJid = JidCreate.entityBareFromOrThrowUnchecked(peerAddress);
        roster = Roster.getInstanceFor(messenger.getConnectionManager().getConnection(accountId).getConnection());
        roster.addPresenceEventListener(presenceEventListener);
        contactAddress.setValue(peerAddress);
        contactAccountId.setValue(accountId);
        disposable.add(peerRepository.observePeerByAddress(accountId, peerJid)
                .subscribeOn(schedulers.getIoScheduler())
                .observeOn(schedulers.getUiScheduler())
                .subscribe(peerOptional -> {
                    if (!peerOptional.isPresent()) {
                        return;
                    }
                    Peer peer = peerOptional.getItem();
                    contactAccountAddress.setValue(peer.getAccount().getAddress());
                    contactAvatar.setValue(new AvatarDrawable(peer.getDisplayName(), peer.getAddress()));
                    contactName.setValue(peer.getDisplayName());

                    RosterEntry entry = roster.getEntry(JidCreate.entityBareFromOrThrowUnchecked(peerAddress));
                    if (entry != null) {
                        List<RosterGroup> groups = entry.getGroups();
                        List<String> groupNames = new ArrayList<>(groups.size());
                        for (RosterGroup g : groups) {
                            groupNames.add(g.getName());
                        }
                        contactGroups.postValue(groupNames);
                    }
                }));

        Presence presence = roster.getPresence(peerJid);
        if (presence != null) {
            contactPresenceMode.postValue(presence.getMode());
            contactPresenceStatus.postValue(presence.getStatus());
        }

        disposable.add(openPgpRepository.observeFingerprints(accountId, peerJid)
                .subscribeOn(schedulers.getIoScheduler())
                .observeOn(schedulers.getUiScheduler())
                .subscribe(list -> contactFingerprints.setValue(list)));
    }

    public LiveData<String> getContactAddress() {
        return contactAddress;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        disposable.dispose();
        if (roster != null) {
            roster.removePresenceEventListener(presenceEventListener);
        }
    }

    public LiveData<UUID> getAccountId() {
        return contactAccountId;
    }

    public LiveData<Drawable> getContactAvatar() {
        return contactAvatar;
    }

    public LiveData<Presence.Mode> getContactPresenceMode() {
        return contactPresenceMode;
    }

    public LiveData<String> getContactName() {
        return contactName;
    }

    public LiveData<String> getContactPresenceStatus() {
        return contactPresenceStatus;
    }

    public LiveData<String> getContactAccountAddress() {
        return contactAccountAddress;
    }

    public LiveData<List<String>> getContactGroups() {
        return contactGroups;
    }

    private final PresenceEventListener presenceEventListener = new CombinedPresenceListener() {
        @Override
        public void presenceReceived(Jid address, Presence presence) {
            if (presence.getFrom().asBareJid().toString().equals(getContactAddress().getValue())) {
                contactPresenceMode.postValue(presence.getMode());
                contactPresenceStatus.postValue(presence.getStatus());
            }
        }
    };

    public void changeContactName(String newName)
            throws XMPPException.XMPPErrorException, SmackException.NotConnectedException,
            InterruptedException, SmackException.NoResponseException {
        if (!newName.trim().isEmpty()) {
            RosterEntry entry = roster.getEntry(JidCreate.entityBareFromOrThrowUnchecked(getContactAddress().getValue()));
            entry.setName(newName);
        }
    }

    public Completable addContactToRosterGroup() {
        return Completable.fromAction(() -> doAddContactToRosterGroup());
    }

    private void doAddContactToRosterGroup() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
        String groupName = "Mercury Seven";
        RosterGroup group = roster.getGroup(groupName);
        if (group == null) {
            group = roster.createGroup(groupName);
        }
        BareJid jid = JidCreate.entityBareFromOrThrowUnchecked(getContactAddress().getValue());
        if (group.contains(jid)) {
            return;
        }

        RosterEntry entry = roster.getEntry(jid);
        group.addEntry(entry);
    }

    public Completable removeContactFromRosterGroup(String group) {
        return Completable.fromAction(() -> roster.getGroup(group).removeEntry(roster.getEntry(JidCreate.entityBareFromOrThrowUnchecked(getContactAddress().getValue()))));

    }

    public LiveData<List<FingerprintViewItem>> getContactFingerprints() {
        return contactFingerprints;
    }

    public void markFingerprintTrusted(OpenPgpV4Fingerprint fingerprint, boolean checked) {
        openPgpRepository.storeTrust(contactAccountId.getValue(),
                JidCreate.entityBareFromOrThrowUnchecked(contactAddress.getValue()),
                fingerprint,
                checked ? OpenPgpTrustStore.Trust.trusted : OpenPgpTrustStore.Trust.untrusted)
        .subscribe();
    }
}
