package com.aterve.siwg;

import android.app.Activity;
import android.content.Intent;
import androidx.annotation.NonNull;
import android.util.Log;
import android.view.Gravity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

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.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.Continuation;

import java.io.IOException;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;

public class SiwgJNI {

    private static final String TAG = "SiwgJNI";

    // Request code used to invoke sign in user interactions.
    private static final int RC_UNUSED = 5001;
    private static final int RC_SIGN_IN = 9001;

    //
    // Global Constants (Same as in sigw.h)
    //

    private static final int MSG_SIGN_IN = 1;
    private static final int MSG_SILENT_SIGN_IN = 2;
    private static final int MSG_SIGN_OUT = 3;

    private static final int STATUS_SUCCESS = 1;
    private static final int STATUS_FAILED = 2;

    //
    // Global Properties
    //
    
    private Activity activity;
    private String client_id;
    private boolean is_supported;

    //
    // Client Handles
    //

    private GoogleSignInAccount mSignedInAccount = null;
    private GoogleSignInOptions mSignInOptions;
    private GoogleSignInClient mGoogleSignInClient;

    // JNI Add to Queue
    public static native void siwgAddToQueue(int msg, String json);

    //
    // Constructor
    //

    public SiwgJNI(Activity activity, String client_id) {
        this.activity = activity;
        this.client_id = client_id;

        // Check if device supports google sign in
        this.is_supported = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(activity) == ConnectionResult.SUCCESS;

        // Create GoogleSignIn Client
        this.mGoogleSignInClient = GoogleSignIn.getClient(activity, getSignInOptions());
    }

    //
    // Event Listeners
    //

    private OnFailureListener newOnFailureListener(final int messageId, final String message) {
        return new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                sendCallbackMessage(messageId, "status", STATUS_FAILED, "error", message);
            }
        };
    }

    private OnSuccessListener<Intent> newOnSuccessListenerForIntent(final int requestCode) {
        return new OnSuccessListener<Intent>() {
            @Override
            public void onSuccess(Intent intent) {
                activity.startActivityForResult(intent, requestCode);
            }
        };
    }

    //
    // Callback Message Methods
    //

    private void sendCallbackMessage(int msg, String key_1, int value_1) {
        String message = null;
        try {
            JSONObject obj = new JSONObject();
            obj.put(key_1, value_1);
            message = obj.toString();
        } catch (JSONException e) {
            message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
        }
        siwgAddToQueue(msg, message);
    }

    private void sendCallbackMessage(int msg, String key_1, int value_1, String key_2, String value_2) {
        String message = null;
        try {
            JSONObject obj = new JSONObject();
            obj.put(key_1, value_1);
            obj.put(key_2, value_2);
            message = obj.toString();
        } catch (JSONException e) {
            message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
        }
        siwgAddToQueue(msg, message);
    }

    private void sendCallbackMessage(int msg, String key_1, int value_1, String key_2, int value_2, String key_3, String value_3) {
        String message = null;
        try {
            JSONObject obj = new JSONObject();
            obj.put(key_1, value_1);
            obj.put(key_2, value_2);
            obj.put(key_3, value_3);
            message = obj.toString();
        } catch (JSONException e) {
            message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
        }
        siwgAddToQueue(msg, message);
    }

    private void onConnected(GoogleSignInAccount googleSignInAccount, final int msg) {
        if (mSignedInAccount != googleSignInAccount) {
            mSignedInAccount = googleSignInAccount;
            sendCallbackMessage(msg, "status", STATUS_SUCCESS);
        }
    }

    private GoogleSignInOptions getSignInOptions() {
        if (mSignInOptions == null) {
            
            // Create Google Sign in (Default)
            GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN);

            builder.requestIdToken(client_id);
            builder.requestServerAuthCode(client_id);
            
            // Request email
            builder.requestEmail();

            mSignInOptions = builder.build();
        }

        return mSignInOptions;
    }

    public void activityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == RC_SIGN_IN) {
            if (intent != null) {
                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(intent);

                if (task.isSuccessful()) {
                    onConnected(task.getResult(), MSG_SIGN_IN);
                } else {
                    sendCallbackMessage(MSG_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Sign-in Failed. Task Not Successfull: " + Integer.toString(resultCode));
                }

            } else {
                sendCallbackMessage(MSG_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Sign-in Failed. Intent does not exist!");
            }
        }
    }

    public void silentLogin() {
        this.activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                
                GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(activity);
                
                if (GoogleSignIn.hasPermissions(account, getSignInOptions().getScopeArray())) {
                    onConnected(account, MSG_SILENT_SIGN_IN);
                } else {

                    mGoogleSignInClient.silentSignIn().addOnCompleteListener(activity,
                        new OnCompleteListener<GoogleSignInAccount>() {
                            @Override
                            public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
                                if (task.isSuccessful()) {
                                    onConnected(task.getResult(), MSG_SILENT_SIGN_IN);
                                } else {
                                    sendCallbackMessage(MSG_SILENT_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Silent Sign-in Failed.");
                                }
                            }
                        });
                }
            }
        });
    }

    //
    // JNI Passed Methods
    //

    public void login() {
        Intent intent = mGoogleSignInClient.getSignInIntent();
        this.activity.startActivityForResult(intent, RC_SIGN_IN);
    }

    public void logout() {
        mGoogleSignInClient.signOut().addOnCompleteListener(this.activity,
            new OnCompleteListener<Void>() {
                @Override
                public void onComplete(@NonNull Task<Void> task) {
                    mSignedInAccount = null;
                    sendCallbackMessage(MSG_SIGN_OUT, "status", STATUS_SUCCESS);
                }
            });
    }

    public String getDisplayName() {
        return isLoggedIn() ? mSignedInAccount.getDisplayName() : null;
    }

    public String getEmail() {
        return isLoggedIn() ? mSignedInAccount.getEmail() : null;
    }

    public String getId() {
        return isLoggedIn() ? mSignedInAccount.getId() : null;
    }

    public String getIdToken() {
        return isLoggedIn() ? mSignedInAccount.getIdToken() : null;
    }

    public String getServerAuthCode() {
        return isLoggedIn() ? mSignedInAccount.getServerAuthCode() : null;
    }

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

    public boolean isSupported() {
        return is_supported;
    }

}
