package de.sebse.fuplanner.services.GoogleAuth;

import android.content.Intent;
import android.content.IntentSender;
import android.util.Log;
import android.widget.Toast;

import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.credentials.CredentialRequest;
import com.google.android.gms.auth.api.credentials.CredentialsClient;
import com.google.android.gms.auth.api.credentials.CredentialsOptions;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.ResolvableApiException;

import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;

import static android.app.Activity.RESULT_OK;

/**
 * Created by Sebastian on 06.11.2017.
 */

public class GoogleAuth {
    // https://developers.google.com/identity/smartlock-passwords/android/retrieve-credentials

    private static final String TAG = "GoogleAuth";
    private final FragmentActivity activity;
    private CredentialsClient mCredentialsClient;
    private boolean mIsResolving;
    @Nullable
    private CredentialsListener mCredentialsListener;
    private boolean isConnected = false;

    public GoogleAuth(FragmentActivity activity) {
        this.activity = activity;
    }

    private void connect() {
        if (this.isUnavailable()) {
            Log.d(TAG, "STATUS: Google auth not available!");
            return;
        }
        this.mCredentialsClient = getClient();
        this.isConnected = true;
    }




    public void getLoginState(final CredentialsListener credentialsListener) {
        if (this.isUnavailable()) {
            Log.d(TAG, "STATUS: Google auth not available!");
            credentialsListener.onCredentials(null);
            return;
        }
        if (!this.isConnected)
            connect();
        CredentialRequest request = new CredentialRequest.Builder()
                .setPasswordLoginSupported(true)
                .build();


        mCredentialsClient.request(request).addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                // Successfully read the credential without any user interaction, this
                // means there was only a single credential and the user has auto
                // sign-in enabled.
                Credential credential = task.getResult().getCredential();
                credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
                return;
            }

            Exception e = task.getException();
            if (e instanceof ResolvableApiException) {
                // This is most likely the case where the user has multiple saved
                // credentials and needs to pick one. This requires showing UI to
                // resolve the read request.
                GoogleAuth.this.mCredentialsListener = credentialsListener;
                ResolvableApiException rae = (ResolvableApiException) e;
                resolveResult(rae, RequestCode.RC_READ);
                return;
            }

            if (e instanceof ApiException) {
                ApiException ae = (ApiException) e;
                if (ae.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
                    // This means only a hint is available, but we are handling that
                    // elsewhere so no need to act here.
                } else {
                    credentialsListener.onCredentials(null);
                    Log.w(TAG, "Unexpected status code: " + ae.getStatusCode());
                }
            }
        });
    }

    public void setLoginState(String username, String password) {
        if (this.isUnavailable()) {
            Log.d(TAG, "STATUS: Google auth not available!");
            Toast.makeText(activity, "Google auth not available!", Toast.LENGTH_SHORT).show();
            return;
        }
        if (!this.isConnected)
            connect();
        Credential credential = new Credential.Builder(username)
                .setPassword(password)
                .build();
        mCredentialsClient.save(credential).addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                showToast("Credential saved.");
                return;
            }

            Exception e = task.getException();
            if (e instanceof ResolvableApiException) {
                // The first time a credential is saved, the user is shown UI
                // to confirm the action. This requires resolution.
                ResolvableApiException rae = (ResolvableApiException) e;
                resolveResult(rae, RequestCode.RC_SAVE);
            } else {
                // Save failure cannot be resolved.
                Log.w(TAG, "Save failed.", e);
                showToast("Credential Save Failed");
            }
        });
    }

    public void deleteLoginState(String username, String password) {
        if (this.isUnavailable()) {
            Log.d(TAG, "STATUS: Google auth not available!");
            return;
        }
        if (!this.isConnected)
            connect();
        Credential credential = new Credential.Builder(username)
                .setPassword(password)
                .build();
        mCredentialsClient.delete(credential).addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                // Credential was deleted successfully
            }
        });
    }


    private boolean isUnavailable() {
        return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this.activity) != ConnectionResult.SUCCESS;
    }

    private CredentialsClient getClient() {
        CredentialsOptions options = new CredentialsOptions.Builder()
                .forceEnableSaveDialog()
                .build();
        return com.google.android.gms.auth.api.credentials.Credentials.getClient(this.activity, options);
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" + data);

        switch (requestCode) {
            case RequestCode.RC_HINT:
                // Drop into handling for RC_READ
            case RequestCode.RC_READ:
                if (resultCode == RESULT_OK) {
                    boolean isHint = (requestCode == RequestCode.RC_HINT);
                    Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
                    if (mCredentialsListener != null)
                        this.mCredentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
                    else
                        Log.d(TAG, "No Credentials Listener");
                } else {
                    if (mCredentialsListener != null)
                        this.mCredentialsListener.onCredentials(null);
                    else
                        Log.d(TAG, "No Credentials Listener");
                    Log.e(TAG, "Credential Read: NOT OK");
                    showToast("Credential Read Failed");
                }

                mIsResolving = false;
                break;
            case RequestCode.RC_SAVE:
                if (resultCode == RESULT_OK) {
                    Log.d(TAG, "Credential Save: OK");
                    showToast("Credential Save Success");
                } else {
                    Log.e(TAG, "Credential Save: NOT OK");
                    showToast("Credential Save Failed");
                }

                mIsResolving = false;
                break;
        }
    }


    private void resolveResult(ResolvableApiException rae, int requestCode) {
        // We don't want to fire multiple resolutions at once since that can result
        // in stacked dialogs after rotation or another similar event.
        if (mIsResolving) {
            Log.w(TAG, "resolveResult: already resolving.");
            return;
        }

        Log.d(TAG, "Resolving: " + rae);
        try {
            rae.startResolutionForResult(this.activity, requestCode);
            mIsResolving = true;
        } catch (IntentSender.SendIntentException e) {
            Log.e(TAG, "STATUS: Failed to send resolution.", e);
        }
    }

    /** Display a short Toast message **/
    private void showToast(String msg) {
        Toast.makeText(this.activity, msg, Toast.LENGTH_SHORT).show();
    }
}
