// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.plugin.authentication.facades;

import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.SettableFuture;
import com.microsoft.alm.auth.PromptBehavior;
import com.microsoft.alm.auth.oauth.OAuth2Authenticator;
import com.microsoft.alm.auth.pat.VstsPatAuthenticator;
import com.microsoft.alm.plugin.authentication.AuthHelper;
import com.microsoft.alm.plugin.authentication.AuthenticationInfo;
import com.microsoft.alm.provider.JaxrsClientProvider;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.TokenPair;
import com.microsoft.alm.secret.VsoTokenScope;
import com.microsoft.alm.storage.InsecureInMemoryStore;
import com.microsoft.alm.storage.SecretStore;
import com.microsoft.visualstudio.services.account.webapi.AccountHttpClient;
import com.microsoft.visualstudio.services.account.webapi.model.Profile;

import javax.ws.rs.client.Client;

public class VsoAuthInfoProvider implements AuthenticationInfoProvider {

    private static final String CLIENT_ID = "502ea21d-e545-4c66-9129-c352ec902969";
    private static final String REDIRECT_URL = "https://xplatalm.com";

    final SecretStore<TokenPair> accessTokenStore;
    final SecretStore<Token> tokenStore;
    final VstsPatAuthenticator vstsPatAuthenticator;
    final JaxrsClientProvider jaxrsClientProvider;

    private VsoAuthInfoProvider() {
        accessTokenStore = new InsecureInMemoryStore<TokenPair>();
        tokenStore = new InsecureInMemoryStore<Token>();

        final OAuth2Authenticator oAuth2Authenticator = OAuth2Authenticator.getAuthenticator(
                CLIENT_ID, REDIRECT_URL, accessTokenStore);
        jaxrsClientProvider = new JaxrsClientProvider(oAuth2Authenticator);

        vstsPatAuthenticator = new VstsPatAuthenticator(CLIENT_ID, REDIRECT_URL, accessTokenStore, tokenStore);

    }

    private static class VsoJavaFxExperimentalAuthInfoProviderHolder {
        public static VsoAuthInfoProvider INSTANCE = new VsoAuthInfoProvider();
    }

    public static VsoAuthInfoProvider getProvider() {
        return VsoJavaFxExperimentalAuthInfoProviderHolder.INSTANCE;
    }

    @Override
    public void getAuthenticationInfoAsync(final String serverUri, final AuthenticationInfoCallback callback) {
        final SettableFuture<AuthenticationInfo> authenticationInfoFuture = SettableFuture.<AuthenticationInfo>create();

        try {
            final Client client = jaxrsClientProvider.getClient();

            //TODO: this is a dependency on the aad-pat-generator jar which is going away.  When we update to consume
            //v0.4.3 Rest client, we can get rid of this one

            //Or we could reconsider the name of the token.  Now we call Profile endpoint just to get the email address
            //which is used in token description, but do we need it?  User can only view PATs after they login, and
            //at that time user knows which account/email they are logged in under already.  So the email provides
            //no additional value.
            final AccountHttpClient accountHttpClient
                    = new AccountHttpClient(client, OAuth2Authenticator.APP_VSSPS_VISUALSTUDIO);

            final Profile me = accountHttpClient.getMyProfile();
            final String emailAddress = me.getCoreAttributes().getEmailAddress().getValue();

            final String tokenDescription = AuthHelper.getTokenDescription(emailAddress);

            final Token token = vstsPatAuthenticator.getPersonalAccessToken(
                    VsoTokenScope.or(VsoTokenScope.CodeAll, VsoTokenScope.WorkRead),
                    tokenDescription,
                    PromptBehavior.AUTO);

            final AuthenticationInfo authenticationInfo = new AuthenticationInfo(me.getId().toString(),
                    token.Value, serverUri, emailAddress);

            authenticationInfoFuture.set(authenticationInfo);

        } catch (Throwable t) {
            authenticationInfoFuture.setException(t);
        }

        Futures.addCallback(authenticationInfoFuture, callback);
    }

    @Override
    public void clearAuthenticationInfo(final String serverUri) {
        // Only generates global PAT currently, so ignore serverUri and clear global PAT only
        this.vstsPatAuthenticator.signOut();
    }

}

@@ -431,32 +431,98 @@
 tAuthenticator;%0A
+import com.microsoft.alm.oauth2.useragent.AuthorizationException;%0A
 import com.micro
@@ -2592,16 +2592,114 @@
   try %7B%0A
+            AuthenticationInfo authenticationInfo = null;%0A            String errorMessage = null;%0A
         
@@ -2754,24 +2754,62 @@
 tClient();%0A%0A
+            if (client != null) %7B%0A    
             
@@ -2926,16 +2926,20 @@
         
+ 
+   
 //v0.4.3
@@ -2980,16 +2980,20 @@
 is one%0A%0A
+    
         
@@ -3116,16 +3116,20 @@
         
+   
+ 
 //which 
@@ -3224,16 +3224,20 @@
 in, and%0A
+    
         
@@ -3355,16 +3355,20 @@
         
+    
 //no add
@@ -3390,24 +3390,28 @@
             
+ 
+   
 final Accoun
@@ -3440,16 +3440,20 @@
 pClient%0A
+    
         
@@ -3550,24 +3550,28 @@
             
+   
+ 
 final Profil
@@ -3607,24 +3607,28 @@
 yProfile();%0A
+    
             
@@ -3713,32 +3713,36 @@
 );%0A%0A            
+ 
+   
 final String tok
@@ -3812,24 +3812,28 @@
             
+  
+  
 final Token 
@@ -3885,16 +3885,20 @@
 sToken(%0A
+    
         
@@ -3974,16 +3974,20 @@
 kRead),%0A
+    
         
@@ -4036,16 +4036,20 @@
         
+    
 PromptBe
@@ -4079,32 +4079,52 @@
     
-final AuthenticationInfo
+    if (token != null) %7B%0A                   
  aut
@@ -4203,24 +4203,32 @@
             
+   
+     
 token.Value,
@@ -4258,70 +4258,482 @@
 s);%0A
-%0A            authenticationInfoFuture.set(authenticationInfo);
+                %7D else %7B%0A                    errorMessage = %22Failed to get a Personal Access Token%22;%0A                %7D%0A            %7D else %7B%0A                errorMessage = %22Failed to get authenticated jaxrs client.%22;%0A            %7D%0A%0A            if (authenticationInfo != null) %7B%0A                authenticationInfoFuture.set(authenticationInfo);%0A            %7D else %7B%0A                authenticationInfoFuture.setException(new AuthorizationException(errorMessage));%0A            %7D
 %0A%0A  

Check for null instead of relying on NPE which is a bug