package com.valtech.contactsync;

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ApiClient {
  private static final Gson GSON = new Gson();

  private static final String AUTHORIZE_URL = "https://stage-id.valtech.com/oauth2/authorize";
  private static final String TOKEN_URL = "https://stage-id.valtech.com/oauth2/token";
  private static final String USER_INFO_URL = "https://stage-id.valtech.com/api/users/me";
  private static final String ALL_USER_INFOS_URL = "https://stage-id.valtech.com/api/users";
  private static final String CLIENT_ID = "valtech.contactsync.android";
  private static final String CLIENT_SECRET = "MGIxZmIwMDYtZTVlOS00ZDdiLTgxYjMtMjg2MzdhMTQwNGVh";
  private static final String INITIAL_SCOPE = "email vidp:get_users vidp:offline";
  private static final String FOLLOW_UP_SCOPE = "vidp:get_users";


  public String getAuthorizeUrl() {
    return AUTHORIZE_URL + "?response_type=code&client_id=" + CLIENT_ID + "&scope=" + INITIAL_SCOPE.replace(" ", "%20");
  }

  public TokenResponse getAccessTokenAndRefreshToken(String code) {
    TokenResponse tokenResponse = tokenRequest(
      new BasicNameValuePair("grant_type", "authorization_code"),
      new BasicNameValuePair("code", code));
    return tokenResponse;
  }

  public String getAccessToken(String refreshToken) {
    TokenResponse tokenResponse = tokenRequest(
      new BasicNameValuePair("grant_type", "refresh_token"),
      new BasicNameValuePair("refresh_token", refreshToken),
      new BasicNameValuePair("scope", FOLLOW_UP_SCOPE));
    return tokenResponse.accessToken;
  }

  private TokenResponse tokenRequest(NameValuePair... fields) {
    HttpClient httpClient = new DefaultHttpClient();
    HttpPost httpPost = new HttpPost(TOKEN_URL);

    try {
      List<NameValuePair> nameValuePairs = new ArrayList<>();
      nameValuePairs.add(new BasicNameValuePair("client_id", CLIENT_ID));
      nameValuePairs.add(new BasicNameValuePair("client_secret", CLIENT_SECRET));
      nameValuePairs.addAll(Arrays.asList(fields));

      httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

      HttpResponse response = httpClient.execute(httpPost);

      int statusCode = response.getStatusLine().getStatusCode();
      if (statusCode >= 400 && statusCode < 500) {
        String json = EntityUtils.toString(response.getEntity());
        TokenErrorResponse tokenErrorResponse = GSON.fromJson(json, TokenErrorResponse.class);
        throw new OAuthException(tokenErrorResponse);
      } else if (statusCode >= 500) {
        throw new RuntimeException("Internal server error occurred.");
      }

      String json = EntityUtils.toString(response.getEntity());
      TokenResponse tokenResponse = GSON.fromJson(json, TokenResponse.class);

      return tokenResponse;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public UserInfoResponse getUserInfoMeResource(String accessToken) {
    return getProtectedResource(USER_INFO_URL, accessToken, UserInfoResponse.class);
  }

  private <T> T getProtectedResource(String url, String accessToken, Class<T> clazz) {
    HttpClient httpClient = new DefaultHttpClient();
    HttpGet httpGet = new HttpGet(url);

    try {
      httpGet.setHeader("Authorization", "Bearer " + accessToken);
      HttpResponse response = httpClient.execute(httpGet);

      int statusCode = response.getStatusLine().getStatusCode();
      if (statusCode >= 400 && statusCode < 500) {
        Header wwwAuthenticateHeader = response.getFirstHeader("WWW-Authenticate");
        throw new OAuthException(wwwAuthenticateHeader);
      } else if (statusCode >= 500) {
        throw new RuntimeException("Internal server error occurred.");
      }

      String json = EntityUtils.toString(response.getEntity());
      T typedResponse = GSON.fromJson(json, clazz);

      return typedResponse;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public static class TokenResponse {
    @SerializedName("access_token")
    public String accessToken;

    @SerializedName("refresh_token")
    public String refreshToken;
  }

  public static class TokenErrorResponse {
    public String error;

    @SerializedName("error_description")
    public String error_description;
  }

  public static class UserInfoResponse {
    public String email;
  }

  public static class OAuthException extends RuntimeException {
    public OAuthException(TokenErrorResponse tokenErrorResponse) {
      super("OAuth error occurred on token request ('" + tokenErrorResponse.error + "', '" + tokenErrorResponse.error_description + "').");
    }

    public OAuthException(Header wwwAuthenticateHeader) {
      super("OAuth error occurred when accessing protected resource (" + wwwAuthenticateHeader.getValue() + ")");
    }
  }
}

@@ -107,16 +107,58 @@
 edName;%0A
+import com.google.gson.reflect.TypeToken;%0A
 import o
@@ -622,24 +622,55 @@
 OException;%0A
+import java.lang.reflect.Type;%0A
 import java.
@@ -3671,24 +3671,223 @@
 lass);%0A  %7D%0A%0A
+  public List%3CUserInfoResponse%3E getUserInfoResources(String accessToken) %7B%0A    return getProtectedResource(ALL_USER_INFOS_URL, accessToken, new TypeToken%3CList%3CUserInfoResponse%3E%3E()%7B%7D.getType());%0A  %7D%0A%0A
   private %3CT
@@ -3947,22 +3947,17 @@
 en, 
-Class%3CT%3E clazz
+Type type
 ) %7B%0A
@@ -4674,13 +4674,12 @@
 on, 
-clazz
+type
 );%0A%0A
@@ -5149,32 +5149,32 @@
 rInfoResponse %7B%0A
-
     public Strin
@@ -5182,16 +5182,255 @@
  email;%0A
+    public String name;%0A%0A    @SerializedName(%22country_code%22)%0A    public String countryCode;%0A%0A    @SerializedName(%22phone_number%22)%0A    public String phoneNumber;%0A%0A    @SerializedName(%22fixed_phone_number%22)%0A    public String fixedPhoneNumber;%0A
   %7D%0A%0A  p

Update ApiClient to fetch list of users