package io.capawesome.capacitorjs.plugins.firebase.authentication.handlers;

import android.content.Intent;
import android.util.Log;
import androidx.activity.result.ActivityResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.getcapacitor.JSArray;
import com.getcapacitor.PluginCall;
import com.google.android.gms.auth.GoogleAuthException;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.GoogleAuthProvider;
import io.capawesome.capacitorjs.plugins.firebase.authentication.FirebaseAuthentication;
import io.capawesome.capacitorjs.plugins.firebase.authentication.FirebaseAuthenticationPlugin;
import io.capawesome.capacitorjs.plugins.firebase.authentication.R;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;

public class GoogleAuthProviderHandler {

    private FirebaseAuthentication pluginImplementation;
    private GoogleSignInClient mGoogleSignInClient;

    public GoogleAuthProviderHandler(FirebaseAuthentication pluginImplementation) {
        this.pluginImplementation = pluginImplementation;
        this.mGoogleSignInClient = buildGoogleSignInClient();
    }

    public void signIn(PluginCall call) {
        mGoogleSignInClient = buildGoogleSignInClient(call);
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        pluginImplementation.startActivityForResult(call, signInIntent, "handleGoogleAuthProviderSignInActivityResult");
    }

    public void link(PluginCall call) {
        mGoogleSignInClient = buildGoogleSignInClient(call);
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        pluginImplementation.startActivityForResult(call, signInIntent, "handleGoogleAuthProviderLinkActivityResult");
    }

    public void signOut() {
        mGoogleSignInClient.signOut();
    }

    public void handleOnActivityResult(@NonNull final PluginCall call, @NonNull ActivityResult result, boolean isLink) {
        Intent data = result.getData();
        Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
        try {
            GoogleSignInAccount account = task.getResult(ApiException.class);
            String idToken = account.getIdToken();
            String serverAuthCode = account.getServerAuthCode();
            AuthCredential credential = GoogleAuthProvider.getCredential(idToken, null);
            // Get Access Token and resolve
            new Thread(
                () -> {
                    String accessToken = null;
                    List<String> scopes = new ArrayList<>();
                    scopes.add("oauth2:email");
                    scopes.addAll(getScopesAsList(call));

                    try {
                        accessToken =
                            GoogleAuthUtil.getToken(
                                mGoogleSignInClient.getApplicationContext(),
                                account.getAccount(),
                                String.join(" ", scopes)
                            );
                        // Clears local cache after every login attempt
                        // to ensure permissions changes elsewhere are reflected in future tokens
                        GoogleAuthUtil.clearToken(mGoogleSignInClient.getApplicationContext(), accessToken);
                    } catch (IOException | GoogleAuthException exception) {
                        if (isLink) {
                            pluginImplementation.handleFailedLink(call, null, exception);
                        } else {
                            pluginImplementation.handleFailedSignIn(call, null, exception);
                        }
                        return;
                    }
                    if (isLink) {
                        pluginImplementation.handleSuccessfulLink(call, credential, idToken, null, accessToken, serverAuthCode);
                    } else {
                        pluginImplementation.handleSuccessfulSignIn(call, credential, idToken, null, accessToken, serverAuthCode, null);
                    }
                }
            )
                .start();
        } catch (ApiException exception) {
            if (isLink) {
                pluginImplementation.handleFailedLink(call, null, exception);
            } else {
                pluginImplementation.handleFailedSignIn(call, null, exception);
            }
        }
    }

    private GoogleSignInClient buildGoogleSignInClient() {
        return buildGoogleSignInClient(null);
    }

    private GoogleSignInClient buildGoogleSignInClient(@Nullable PluginCall call) {
        GoogleSignInOptions.Builder googleSignInOptionsBuilder = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(pluginImplementation.getPlugin().getContext().getString(R.string.default_web_client_id))
            .requestServerAuthCode(pluginImplementation.getPlugin().getContext().getString(R.string.default_web_client_id))
            .requestEmail();

        if (call != null) {
            List<String> scopeList = getScopesAsList(call);
            for (String scope : scopeList) {
                googleSignInOptionsBuilder = googleSignInOptionsBuilder.requestScopes(new Scope(scope));
            }
        }

        return GoogleSignIn.getClient(pluginImplementation.getPlugin().getActivity(), googleSignInOptionsBuilder.build());
    }

    private List<String> getScopesAsList(@NonNull PluginCall call) {
        List<String> scopeList = new ArrayList<>();
        JSArray scopes = call.getArray("scopes");
        if (scopes != null) {
            try {
                scopeList = scopes.toList();
            } catch (JSONException exception) {
                Log.e(FirebaseAuthenticationPlugin.TAG, "getScopesAsList failed.", exception);
            }
        }

        return scopeList;
    }
}
