package org.radarcns.oauth;

import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.Route;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Class for handling OAuth2 client credentials grant
 */
public class OAuth2Client {
    private String managementPortalUrl;
    private String clientId;
    private String clientSecret;
    private Set<String> scope;
    private OAuth2AccessToken currentToken;

    public OAuth2Client() {
        this.managementPortalUrl = "";
        this.clientId = "";
        this.clientSecret = "";
        this.scope = new HashSet<>();
        this.currentToken = new OAuth2AccessToken();
    }

    public String getManagementPortalUrl() {
        return managementPortalUrl;
    }

    public String getClientId() {
        return clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public Set<String> getScope() {
        return scope;
    }

    public OAuth2Client managementPortalUrl(String managementPortalUrl) {
        this.managementPortalUrl = managementPortalUrl;
        return this;
    }

    public OAuth2Client clientId(String clientId) {
        this.clientId = clientId;
        return this;
    }

    public OAuth2Client clientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
        return this;
    }

    public OAuth2Client addScope(String scope) {
        this.scope.add(scope);
        return this;
    }

    public OAuth2AccessToken getAccessToken() {
        if (currentToken.isExpired()) {
            getNewToken();
        }
        return currentToken;
    }

    private void getNewToken() {
        // build the form to post to the token endpoint
        FormBody body = new FormBody.Builder().add("grant_type", "client_credentials")
            .add("scope", String.join(" ", scope)).build();

        // build the POST request to the token endpoint with the form data
        Request request = new Request.Builder()
            .addHeader("Accept", "application/json")
            .url(getManagementPortalUrl() + "/oauth/token")
            .post(body)
            .build();

        // create a client which will supply OAuth client id and secret as HTTP basic authentication
        OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(10, TimeUnit.SECONDS)
            .writeTimeout(10, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .authenticator(new Authenticator() {

                private int retries = 0;
                private int maxRetries = 5;

                @Override
                public Request authenticate(Route route, Response response) throws IOException {
                    if (retries >= maxRetries) {
                        return null;
                    }
                    retries++;
                    String credential = Credentials.basic(getClientId(), getClientSecret());
                    return response.request().newBuilder()
                        .header("Authorization", credential)
                        .build();
                }
            }).build();

        // make the client execute the POST request
        try {
            Response response = client.newCall(request).execute();
            currentToken = OAuth2AccessToken.getObject(response);
        }
        catch (IOException e) {
            currentToken = new  OAuth2AccessToken(null, null, 0, null, null, null, 0, null,
                "io_error", e.getMessage(), null);
        }
    }
}


@@ -589,16 +589,62 @@
 Token;%0A%0A
+    private static OkHttpClient HTTP_CLIENT;%0A%0A
     publ
@@ -1833,523 +1833,84 @@
    p
-rivate void getNewToken() %7B%0A        // build the form to post to the token endpoint%0A        FormBody body = new FormBody.Builder().add(%22grant_type%22, %22client_credentials%22)%0A            .add(%22scope%22, String.join(%22 %22, scope)).build();%0A%0A        // build the POST request to the token endpoint with the form data%0A        Request request = new Request.Builder()%0A            .addHeader(%22Accept%22, %22application/json%22)%0A            .url(getManagementPortalUrl() + %22/oauth/token%22)%0A            .post(body)%0A            .build();%0A%0A
+ublic OkHttpClient getHttpClient() %7B%0A        if (HTTP_CLIENT == null) %7B%0A    
     
@@ -2018,27 +2018,23 @@
     
-OkHttpClient client
+    HTTP_CLIENT
  = n
@@ -2071,16 +2071,20 @@
         
+  
+  
 .connect
@@ -2125,16 +2125,20 @@
         
+    
 .writeTi
@@ -2177,16 +2177,20 @@
         
+   
+ 
 .readTim
@@ -2208,32 +2208,36 @@
 meUnit.SECONDS)%0A
+    
             .aut
@@ -2270,16 +2270,20 @@
 or() %7B%0A%0A
+    
         
@@ -2323,32 +2323,36 @@
                 
+    
 private int maxR
@@ -2380,16 +2380,20 @@
         
+    
 @Overrid
@@ -2394,16 +2394,20 @@
 verride%0A
+    
         
@@ -2511,24 +2511,28 @@
             
+    
 if (retries 
@@ -2568,24 +2568,28 @@
             
+  
+  
 return null;
@@ -2589,16 +2589,20 @@
 n null;%0A
+    
         
@@ -2635,16 +2635,20 @@
         
+    
 retries+
@@ -2650,16 +2650,20 @@
 ries++;%0A
+    
         
@@ -2747,16 +2747,20 @@
 ret());%0A
+    
         
@@ -2834,16 +2834,20 @@
         
+    
 .header(
@@ -2899,16 +2899,20 @@
         
+    
 .build()
@@ -2909,24 +2909,28 @@
   .build();%0A
+    
             
@@ -2951,10 +2951,578 @@
     
-%7D)
+    %7D).build();%0A        %7D%0A        return HTTP_CLIENT;%0A    %7D%0A%0A    private void getNewToken() %7B%0A        // build the form to post to the token endpoint%0A        FormBody body = new FormBody.Builder().add(%22grant_type%22, %22client_credentials%22)%0A            .add(%22scope%22, String.join(%22 %22, scope)).build();%0A%0A        // build the POST request to the token endpoint with the form data%0A        Request request = new Request.Builder()%0A            .addHeader(%22Accept%22, %22application/json%22)%0A            .url(getManagementPortalUrl() + %22/oauth/token%22)%0A            .post(body)%0A            
 .bui
@@ -3626,22 +3626,31 @@
 ponse = 
-c
+getHttpC
 lient
+()
 .newCall

Update OAuth2Client to have a singleton OkHttpClient