/*
 * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license.
 * See LICENSE in the project root for license information.
 */
package com.microsoft.graph.snippets;

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.microsoft.graph.models.extensions.User;
import com.microsoft.graph.snippets.util.IManifestReader;
import com.microsoft.graph.snippets.util.ManifestReader;
import com.microsoft.graph.snippets.util.SharedPrefsUtil;
import com.microsoft.identity.client.AuthenticationResult;
import com.microsoft.identity.client.IAccount;
import com.microsoft.identity.client.Logger;
import com.microsoft.identity.client.exception.MsalClientException;
import com.microsoft.identity.client.exception.MsalException;
import com.microsoft.identity.client.exception.MsalServiceException;
import com.microsoft.identity.client.exception.MsalUiRequiredException;
import java.net.URI;
import java.util.List;
import java.util.UUID;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static com.microsoft.graph.snippets.R.id.layout_diagnostics;
import static com.microsoft.graph.snippets.R.id.o365_signin;
import static com.microsoft.graph.snippets.R.id.view_diagnosticsdata;
import static com.microsoft.graph.snippets.R.layout.activity_signin;
import static com.microsoft.graph.snippets.R.string.sign_in_err;
import static com.microsoft.graph.snippets.R.string.warning_client_id_redirect_uri_incorrect;

public class SignInActivity
        extends BaseActivity
        implements MSALAuthenticationCallback {

    private boolean mEnablePiiLogging = false;
    private static final String TAG = "SignInActivity";

    @BindView(layout_diagnostics)
    protected View mDiagnosticsLayout;

    @BindView(view_diagnosticsdata)
    protected TextView mDiagnosticsTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(activity_signin);
        setTitle(R.string.app_name);
        ButterKnife.bind(this);
    }

    @OnClick(o365_signin)
    public void onSignInO365Clicked() {
        try {
            authenticate();
        } catch (IllegalArgumentException e) {
            warnBadClient();
        }
    }

    @Override
    public void onSuccess(AuthenticationResult authenticationResult) {
        // reset anything that may have gone wrong...
        mDiagnosticsLayout.setVisibility(INVISIBLE);
        mDiagnosticsTxt.setText("");

        // get rid of this Activity so that users can't 'back' into it
        finish();

        // save our auth token to use later
        SharedPrefsUtil.persistAuthToken(authenticationResult);

        // get the user display name
        final String userDisplayableId =
                authenticationResult
                        .getAccount()
                        .getUsername();

        // get the index of their '@' in the name (to determine domain)
        final int at = userDisplayableId.indexOf("@");

        // parse-out the tenant
        final String tenant = userDisplayableId.substring(at + 1);

        SharedPrefsUtil.persistUserTenant(tenant);
        SharedPrefsUtil.persistUserID(authenticationResult);

        // go to our main activity
        start();
    }

    @Override
    public void onError(MsalException e) {
        e.printStackTrace();

        //Show the localized message supplied with the exception or
        //or a default message from the string resources if a
        //localized message cannot be obtained
        String msg;
        if (null == (msg = e.getLocalizedMessage())) {
            msg = getString(sign_in_err);
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        } else {
            mDiagnosticsTxt.setText(msg);
            mDiagnosticsLayout.setVisibility(VISIBLE);
        }

        if (e instanceof MsalClientException) {
            // This means errors happened in the sdk itself, could be network, Json parse, etc. Check MsalError.java
            // for detailed list of the errors.

            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();

        } else if (e instanceof MsalServiceException) {
            // This means something is wrong when the sdk is communication to the service, mostly likely it's the client
            // configuration.
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();

        } else if (e instanceof MsalUiRequiredException) {
            // This explicitly indicates that developer needs to prompt the user, it could be refresh token is expired, revoked
            // or user changes the password; or it could be that no token was found in the token cache.
            AuthenticationManager mgr = AuthenticationManager.getInstance();
            mgr.callAcquireToken(SignInActivity.this, this);
        }

    }

    private void warnBadClient() {
        Toast.makeText(this,
                warning_client_id_redirect_uri_incorrect,
                Toast.LENGTH_LONG)
                .show();
    }

    private void authenticate() throws IllegalArgumentException {
        validateOrganizationArgs();
        connect();
        AuthenticationManager.getInstance();
    }

    private void connect() {

        // The sample app is having the PII enable setting on the MainActivity. Ideally, app should decide to enable Pii or not,
        // if it's enabled, it should be  the setting when the application is onCreate.
        if (mEnablePiiLogging) {
            Logger.getInstance().setEnablePII(true);
        } else {
            Logger.getInstance().setEnablePII(false);
        }

        AuthenticationManager mgr = AuthenticationManager.getInstance();

        /* Attempt to get a user and acquireTokenSilent
         * If this fails we do an interactive request
         */
        List<IAccount> users = null;

        try {
            users = mgr.getPublicClient().getAccounts();

            if (users != null && users.size() == 1) {
                /* We have 1 user */
                mUser = users.get(0);
                mgr.callAcquireTokenSilent(
                        mUser,
                        true,
                        this);
            } else {
                /* We have no user */

                /* Let's do an interactive request */
                mgr.callAcquireToken(
                        this,
                        this);
            }
        }
        
        catch (IndexOutOfBoundsException e) {
            Log.d(TAG, "User at this position does not exist: " + e.toString());
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();


        } catch (IllegalStateException e) {
            Log.d(TAG, "MSAL Exception Generated: " + e.toString());
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();


        } catch (Exception e) {
            Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void validateOrganizationArgs() throws IllegalArgumentException {
        IManifestReader metaDataReader = new ManifestReader();
        UUID.fromString(metaDataReader.getApplicationMetadataValueString("com.microsoft.identity.client.ClientId"));
        URI.create(ServiceConstants.REDIRECT_URI);
    }

    private void start() {
        Intent appLaunch = new Intent(this, SnippetListActivity.class);
        startActivity(appLaunch);
    }

    public IAccount mUser;

    @Override
    public void onError(Exception exception) {
        Toast.makeText(this, exception.getMessage(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCancel() {
        Toast.makeText(this, "User cancelled the flow.", Toast.LENGTH_SHORT).show();
    }
    /**
     * Handles redirect response from https://login.microsoftonline.com/common and
     * notifies the MSAL library that the user has completed the authentication
     * dialog
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (AuthenticationManager
                .getInstance()
                .getPublicClient() != null) {
            AuthenticationManager
                    .getInstance()
                    .getPublicClient()
                    .handleInteractiveRequestRedirect(requestCode, resultCode, data);
        }
    }

}