package org.mercury_im.messenger.usecase;

import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLErrorException;
import org.mercury_im.messenger.xmpp.MercuryConnection;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import io.reactivex.Completable;
import io.reactivex.Single;

public class LogIntoAccount {

    public enum ConnectionResult {
        success,
        credential_error,
        server_error,
        other_error
    }

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

    private final MercuryConnection connection;

    private LogIntoAccount(MercuryConnection connection) {
        this.connection = connection;
    }

    public static LogIntoAccount with(MercuryConnection connection) {
        if (connection == null) {
            throw new NullPointerException("MercuryConnection cannot be null.");
        }
        return new LogIntoAccount(connection);
    }

    public Completable executeAndPossiblyThrow() {
        return Completable.fromAction(this::doAuthenticateIfNecessary);
    }

    public Single<ConnectionResult> execute() {
        return Single.fromCallable(this::authenticateIfNecessary);
    }

    private ConnectionResult authenticateIfNecessary() {
        try {
            doAuthenticateIfNecessary();
            return ConnectionResult.success;
        } catch (SASLErrorException e) {
            LOGGER.log(Level.WARNING, "SASL Error while connecting to account " + connection.getAccount().getId(), e);
            return ConnectionResult.credential_error;
        } catch (SmackException.ConnectionException e) {
            LOGGER.log(Level.WARNING, "Connectivity error while connecting to account " + connection.getAccount().getId(), e);
            return ConnectionResult.server_error;
        }
        catch (IOException | XMPPException | SmackException | InterruptedException e) {
            LOGGER.log(Level.WARNING, "Error connecting to account " + connection.getAccount().getId(), e);
            return ConnectionResult.other_error;
        }
    }

    private void doAuthenticateIfNecessary()
            throws InterruptedException, XMPPException, SmackException, IOException {
        if (!connection.getConnection().isAuthenticated()) {
            LOGGER.log(Level.INFO, "Logging in");
            ((AbstractXMPPConnection) connection.getConnection()).connect().login();
            LOGGER.log(Level.INFO, "Login complete");
        }
    }

}
