package foundation.e.mail.account;


import java.util.HashMap;
import java.util.Map;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.content.Context;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;

import foundation.e.mail.activity.Accounts;

import foundation.e.mail.mail.AuthenticationFailedException;
import foundation.e.mail.mail.OAuth2NeedUserPromptException;
import foundation.e.mail.mail.oauth.OAuth2TokenProvider;

import static foundation.e.mail.account.OAuthConstants.ACCOUNT_EMAIL_ADDRESS_KEY;
import static foundation.e.mail.account.OAuthConstants.AUTH_TOKEN_TYPE;
import static foundation.e.mail.account.OAuthConstants.EELO_ACCOUNT_TYPE;
import static foundation.e.mail.account.OAuthConstants.GOOGLE_ACCOUNT_TYPE;

public class K9OAuth2TokenProvider extends OAuth2TokenProvider {

    private static final String OFFICIAL_GOOGLE_ACCOUNT_TYPE = "com.google";

    private AccountManager accountManager;
    private String authToken = null;

    private AndroidAccountOAuth2TokenStore gmailTokenProviderWithAccountSystem;
    private K9OAuth2AuthorizationCodeFlowTokenProvider authorizationCodeFlowTokenProvider;
    private Oauth2PromptRequestHandler promptRequestHandler;

    public K9OAuth2TokenProvider(Context context) {
        accountManager = AccountManager.get(context);
        gmailTokenProviderWithAccountSystem = new AndroidAccountOAuth2TokenStore(context);
        authorizationCodeFlowTokenProvider = new K9OAuth2AuthorizationCodeFlowTokenProvider(context);
    }

    public K9OAuth2AuthorizationCodeFlowTokenProvider getAuthorizationCodeFlowTokenProvider() {
        return authorizationCodeFlowTokenProvider;
    }

    private Account getAccountFromManager(String emailAddress) {
        android.accounts.Account[] accounts = accountManager.getAccountsByType(OFFICIAL_GOOGLE_ACCOUNT_TYPE);
        for (android.accounts.Account account : accounts) {
            if (account.name.equals(emailAddress)) {
                return account;
            }
        }
        return null;
    }


    @Override
    public String getToken(String email, long timeoutMillis)
	    throws AuthenticationFailedException, OAuth2NeedUserPromptException {
        String authToken = getTokenFromAccountManager(email);
        if (authToken != null) {
            return authToken;
        }

	Account gmailAccount = getAccountFromManager(email);
        if (gmailAccount != null) {
            return gmailTokenProviderWithAccountSystem.getToken(email, gmailAccount, timeoutMillis);
        }

        return authorizationCodeFlowTokenProvider.getToken(email, timeoutMillis);
    }

    private synchronized String getTokenFromAccountManager(String emailId) {
        authToken = null; // set null otherwise we will have issues when address A is on account manager but Address B isn't, authToken will be != null (token of address A)
        android.accounts.Account[] eeloAccounts = accountManager.getAccountsByType(
                EELO_ACCOUNT_TYPE);
        android.accounts.Account[] googleAccounts = accountManager.getAccountsByType(
                GOOGLE_ACCOUNT_TYPE);

        ArrayList<Account> accounts = new ArrayList<>();
        for (Account eeloAccount : eeloAccounts) {
            accounts.add(eeloAccount);
        }
        for (Account googleAccount : googleAccounts) {
            accounts.add(googleAccount);
        }

        if (accounts.size() > 0) {
            for (Account account : accounts) {
                String accountEmailId = accountManager.getUserData(account,
                        ACCOUNT_EMAIL_ADDRESS_KEY);
                if (emailId.equals(accountEmailId)) {
                    final CountDownLatch countDownLatch = new CountDownLatch(1);
                    accountManager.getAuthToken(account, AUTH_TOKEN_TYPE, new Bundle(),
                            null, new AccountManagerCallback<Bundle>() {
                                @Override
                                public void run(AccountManagerFuture<Bundle>
                                                        accountManagerFuture) {
                                    try {
                                        authToken = accountManagerFuture.getResult().getString(
                                                AccountManager.KEY_AUTHTOKEN);
                                        countDownLatch.countDown();
                                    } catch (Exception e) {
                                        authToken = null;
                                    }
                                }
                            }, null);
                    try {
                        countDownLatch.await();
                    }
                    catch (Exception e)
                    {
                        authToken = null;
                    }
                }
            }
        } else {
            authToken = null;
        }
        return authToken;
    }

    @Override
    public void invalidateToken(String email) {
        String authToken = getTokenFromAccountManager(email);
        if (authToken != null) {
            accountManager.invalidateAuthToken(AUTH_TOKEN_TYPE,authToken);
            return;

        }
        Account gmailAccount = getAccountFromManager(email);

        if (gmailAccount != null) {
            gmailTokenProviderWithAccountSystem.invalidateAccessToken(email);
        } else {
            authorizationCodeFlowTokenProvider.invalidateAccessToken(email);
        }
    }

    @Override
    public void disconnectEmailWithK9(String email) {
        Account gmailAccount = getAccountFromManager(email);

        if (gmailAccount != null) {
            gmailTokenProviderWithAccountSystem.invalidateAccessToken(email);
        } else {
            authorizationCodeFlowTokenProvider.invalidateRefreshToken(email);
        }
    }

    public void setPromptRequestHandler(Oauth2PromptRequestHandler promptRequestHandler) {
        this.promptRequestHandler = promptRequestHandler;
        gmailTokenProviderWithAccountSystem.setPromptRequestHandler(promptRequestHandler);
        authorizationCodeFlowTokenProvider.setPromptRequestHandler(promptRequestHandler);
    }
}

