package de.sebse.fuplanner.services.kvv;

import android.content.Context;

import org.jetbrains.annotations.NotNull;

import androidx.annotation.Nullable;
import de.sebse.fuplanner.services.fulogin.AccountGeneral;
import de.sebse.fuplanner.services.kvv.types.LoginToken;
import de.sebse.fuplanner.tools.CustomAccountManager;
import de.sebse.fuplanner.tools.NetworkCallbackCollector;
import de.sebse.fuplanner.tools.network.HTTPService;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;

public class Login extends HTTPService {
    private final KVVListener mListener;
    @Nullable private LoginToken mToken;
    private boolean mLoginPending = false;
    private final NetworkCallbackCollector<LoginToken> mRefreshCallbacks = new NetworkCallbackCollector<>();

    Login(KVVListener listener, Context context) {
        super(context);
        this.mListener = listener;
    }

    public void restoreOnlineLogin(BooleanInterface callback) {
        if (mLoginPending) {
            callback.run(false);
            return;
        }
        mLoginPending = true;
        LoginToken.load(mListener.getAccountManager(), token -> {
            boolean result = setToken(token);
            mLoginPending = false;
            callback.run(result);
        });
    }

    public void isOfflineStoredAvailable(BooleanInterface callback) {
        LoginToken.load(mListener.getAccountManager(), token -> {
            callback.run(token != null);
        });
    }

    public boolean logout(boolean delete) {
        if (mLoginPending)
            return false;
        if (mToken == null)
            return true;
        if (delete)
            mToken.delete(mListener.getAccountManager());
        mToken = null;
        return handleCallbacks();
    }

    public boolean isLoginPending() {
        return mLoginPending;
    }

    public boolean isLoggedIn() {
        return mToken != null;
    }

    public boolean isInOnlineMode() {
        return isLoggedIn();
    }

    void testLoginToken(@NotNull NetworkCallback<LoginToken> callback, @NotNull NetworkErrorCallback errorCallback) {
        if (mToken == null) {
            errorCallback.onError(new NetworkError(100173, -1, "Not logged in!"));
            return;
        }
        testLoginToken(mToken, callback, errorCallback);
    }

    private void testLoginToken(@NotNull LoginToken token, @NotNull NetworkCallback<LoginToken> callback, @NotNull NetworkErrorCallback errorCallback) {
        new de.sebse.fuplanner.services.kvv.sync.Login(getContext()).testLoginToken(token, callback, errorCallback);
    }

    @Nullable public LoginToken getLoginToken() {
        return mToken;
    }

    void refreshLogin(NetworkCallback<LoginToken> success, NetworkErrorCallback error) {
        boolean isFirst = mRefreshCallbacks.isEmpty();
        mRefreshCallbacks.add(success, error);
        if (!isFirst)
            return;
        CustomAccountManager manager = mListener.getAccountManager();
        manager.doInvalidateToken(AccountGeneral.ACCOUNT_TYPE, AccountGeneral.AUTHTOKEN_TYPE_KVV, ignored -> {
            restoreOnlineLogin(isRestored -> {
                if (isRestored)
                    testLoginToken(mRefreshCallbacks::responseResponse, mRefreshCallbacks::responseError);
                else {
                    logout(true);
                    mRefreshCallbacks.responseError(new NetworkError(100180, 403, "Re-login failed!"));
                }
            });
        });
    }



    private boolean handleCallbacks() {
        if (mToken != null) {
            mListener.onLogin(mToken);
            return true;
        } else {
            mListener.onLogout();
            return false;
        }
    }

    private boolean setToken(@Nullable LoginToken token) {
        if (token == null)
            return false;
        boolean isOnlyRefresh = mToken != null;
        mToken = token;
        return isOnlyRefresh || handleCallbacks();
    }

    public interface BooleanInterface {
        void run(boolean bool);
    }
}
