/**
  * Log in a user with email/username and password using a DB connection. Example usage:
  *
  * <pre><code>
  * client.login("{username or email}", "{password}")
  *      .setConnection("{database connection name}")
  *      .start(new BaseCallback<Credentials>() {
  *          {@literal}Override
  *          public void onSuccess(Credentials payload) { }
  *
  *          {@literal}Override
  *          public void onFailure(Auth0Exception error) { }
  *      });
  * </code></pre>
  *
  * @param usernameOrEmail of the user depending of the type of DB connection
  * @param password of the user
  * @return a request to configure and start that will yield {@link Credentials}
  */
 @SuppressWarnings("WeakerAccess")
 public AuthenticationRequest login(String usernameOrEmail, String password) {
   Map<String, Object> requestParameters =
       ParameterBuilder.newAuthenticationBuilder()
           .set(USERNAME_KEY, usernameOrEmail)
           .set(PASSWORD_KEY, password)
           .setGrantType(GRANT_TYPE_PASSWORD)
           .asDictionary();
   return loginWithResourceOwner(requestParameters);
 }
 /**
  * Start a passwordless flow with <a
  * href="https://auth0.com/docs/auth-api#!#post--with_sms">SMS</a> Example usage:
  *
  * <pre><code>
  * client.passwordlessWithSms("{phone number}", PasswordlessType.CODE)
  *      .start(new BaseCallback<Void>() {
  *          {@literal}Override
  *          public void onSuccess(Void payload) {}
  *
  *          {@literal}Override
  *          public void onFailure(Auth0Exception error) {}
  *      });
  * </code></pre>
  *
  * @param phoneNumber where an SMS with a verification code will be sent
  * @param passwordlessType indicate whether the SMS should contain a code, link or magic link
  *     (android & iOS)
  * @return a request to configure and start
  */
 @SuppressWarnings("WeakerAccess")
 public ParameterizableRequest<Void> passwordlessWithSMS(
     String phoneNumber, PasswordlessType passwordlessType) {
   final Map<String, Object> parameters =
       ParameterBuilder.newBuilder()
           .set(PHONE_NUMBER_KEY, phoneNumber)
           .setSend(passwordlessType)
           .setConnection(SMS_CONNECTION)
           .asDictionary();
   return passwordless().addParameters(parameters);
 }
  private <T> ParameterizableRequest<T> delegation(Class<T> clazz) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DELEGATION_PATH).build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .setClientId(getClientId())
            .setGrantType(ParameterBuilder.GRANT_TYPE_JWT)
            .asDictionary();

    return factory.POST(url, client, gson, clazz).addParameters(parameters);
  }
  /**
   * Performs a custom <a href="https://auth0.com/docs/auth-api#!#post--delegation">delegation</a>
   * request that will yield a delegation token. Example usage:
   *
   * <pre><code>
   * client.delegation()
   *      .addParameter("api_type", "firebase")
   *      .start(new BaseCallback<Map<String, Object>>() {
   *          {@literal}Override
   *          public void onSuccess(Map<String, Object> payload) {}
   *
   *          {@literal}Override
   *          public void onFailure(Auth0Exception error) {}
   *      });
   * </code></pre>
   *
   * @return a request to configure and start
   */
  @SuppressWarnings("WeakerAccess")
  public ParameterizableRequest<Map<String, Object>> delegation() {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(DELEGATION_PATH).build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .setClientId(getClientId())
            .setGrantType(ParameterBuilder.GRANT_TYPE_JWT)
            .asDictionary();
    return factory.rawPOST(url, client, gson).addParameters(parameters);
  }
  /**
   * Start a passwordless flow with <a
   * href="https://auth0.com/docs/auth-api#!#post--with_email">Email</a> Example usage:
   *
   * <pre><code>
   * client.passwordlessWithEmail("{email}", PasswordlessType.CODE)
   *      .start(new BaseCallback<Void>() {
   *          {@literal}Override
   *          public void onSuccess(Void payload) {}
   *
   *          {@literal}Override
   *          public void onFailure(Auth0Exception error) {}
   *      });
   * </code></pre>
   *
   * @param email that will receive a verification code to use for login
   * @param passwordlessType indicate whether the email should contain a code, link or magic link
   *     (android & iOS)
   * @return a request to configure and start
   */
  @SuppressWarnings("WeakerAccess")
  public ParameterizableRequest<Void> passwordlessWithEmail(
      String email, PasswordlessType passwordlessType) {
    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .set(EMAIL_KEY, email)
            .setSend(passwordlessType)
            .setConnection(EMAIL_CONNECTION)
            .asDictionary();

    return passwordless().addParameters(parameters);
  }
 /**
  * Log in a user using an email and a verification code received via Email (Part of passwordless
  * login flow) Example usage:
  *
  * <pre><code>
  * client.loginWithEmail("{email}", "{code}")
  *      .start(new BaseCallback<Credentials>() {
  *          {@literal}Override
  *          public void onSuccess(Credentials payload) { }
  *
  *          {@literal}@Override
  *          public void onFailure(Auth0Exception error) { }
  *      });
  * </code></pre>
  *
  * @param email where the user received the verification code
  * @param verificationCode sent by Auth0 via Email
  * @return a request to configure and start that will yield {@link Credentials}
  */
 @SuppressWarnings("WeakerAccess")
 public AuthenticationRequest loginWithEmail(String email, String verificationCode) {
   Map<String, Object> parameters =
       ParameterBuilder.newAuthenticationBuilder()
           .set(USERNAME_KEY, email)
           .set(PASSWORD_KEY, verificationCode)
           .setGrantType(GRANT_TYPE_PASSWORD)
           .setClientId(getClientId())
           .setConnection(EMAIL_CONNECTION)
           .asDictionary();
   return loginWithResourceOwner(parameters);
 }
  /**
   * Start a custom passwordless flow
   *
   * @return a request to configure and start
   */
  @SuppressWarnings("WeakerAccess")
  public ParameterizableRequest<Void> passwordless() {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(PASSWORDLESS_PATH)
            .addPathSegment(START_PATH)
            .build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder().setClientId(getClientId()).asDictionary();
    return factory.POST(url, client, gson).addParameters(parameters);
  }
  /**
   * Unlink a user identity calling <a
   * href="https://auth0.com/docs/auth-api#!#post--unlink">'/unlink'</a> endpoint Example usage:
   *
   * <pre><code>
   * client.unlink("{auth0 user id}", "{user access token}")
   *      .start(new BaseCallback<Void>() {
   *          {@literal}Override
   *          public void onSuccess(Void payload) {}
   *
   *          {@literal}Override
   *          public void onFailure(Auth0Exception error) {}
   *      });
   * </code></pre>
   *
   * @param userId of the identity to unlink
   * @param accessToken of the main identity obtained after login
   * @return a request to start
   */
  @SuppressWarnings("WeakerAccess")
  public Request<Void> unlink(String userId, String accessToken) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl()).newBuilder().addPathSegment(UNLINK_PATH).build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .setAccessToken(accessToken)
            .set(CLIENT_ID_KEY, getClientId())
            .set(USER_ID_KEY, userId)
            .asDictionary();

    return factory.POST(url, client, gson).addParameters(parameters);
  }
  private AuthenticationRequest loginWithResourceOwner(Map<String, Object> parameters) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(OAUTH_PATH)
            .addPathSegment(RESOURCE_OWNER_PATH)
            .build();

    final Map<String, Object> requestParameters =
        ParameterBuilder.newBuilder()
            .setClientId(getClientId())
            .setConnection(defaultDatabaseConnection)
            .addAll(parameters)
            .asDictionary();
    return factory
        .authenticationPOST(url, client, gson)
        .addAuthenticationParameters(requestParameters);
  }
  /**
   * Log in a user with a OAuth 'access_token' of a Identity Provider like Facebook or Twitter using
   * <a href="https://auth0.com/docs/auth-api#!#post--oauth-access_token">'\oauth\access_token'
   * endpoint</a> Example usage:
   *
   * <pre><code>
   * client.loginWithOAuthAccessToken("{token}", "{connection name}")
   *      .setConnection("second-database")
   *      .start(new BaseCallback<Credentials>() {
   *          {@literal}Override
   *          public void onSuccess(Credentials payload) { }
   *
   *          {@literal}Override
   *          public void onFailure(Auth0Exception error) { }
   *      });
   * </code></pre>
   *
   * @param token obtained from the IdP
   * @param connection that will be used to authenticate the user, e.g. 'facebook'
   * @return a request to configure and start that will yield {@link Credentials}
   */
  @SuppressWarnings("WeakerAccess")
  public AuthenticationRequest loginWithOAuthAccessToken(String token, String connection) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(OAUTH_PATH)
            .addPathSegment(ACCESS_TOKEN_PATH)
            .build();

    Map<String, Object> parameters =
        ParameterBuilder.newAuthenticationBuilder()
            .setClientId(getClientId())
            .setConnection(connection)
            .setAccessToken(token)
            .asDictionary();

    return factory.authenticationPOST(url, client, gson).addAuthenticationParameters(parameters);
  }
  /**
   * Request a change password using <a
   * href="https://auth0.com/docs/auth-api#!#post--dbconnections-change_password">'/dbconnections/change_password'</a>
   * Example usage:
   *
   * <pre><code>
   * client.requestChangePassword("{email}")
   *      .start(new BaseCallback<Void>() {
   *          {@literal}Override
   *          public void onSuccess(Void payload) {}
   *
   *          {@literal}Override
   *          public void onFailure(Auth0Exception error) {}
   *      });
   * </code></pre>
   *
   * @param email of the user that changes the password. It's also where the email will be sent with
   *     the link to perform the change password.
   * @return a request to configure and start
   */
  @SuppressWarnings("WeakerAccess")
  public DatabaseConnectionRequest<Void> requestChangePassword(String email) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(DB_CONNECTIONS_PATH)
            .addPathSegment(CHANGE_PASSWORD_PATH)
            .build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .set(EMAIL_KEY, email)
            .setClientId(getClientId())
            .setConnection(defaultDatabaseConnection)
            .asDictionary();
    final ParameterizableRequest<Void> request =
        factory.POST(url, client, gson).addParameters(parameters);
    return new DatabaseConnectionRequest<>(request);
  }
  /**
   * Fetch the token information from Auth0, using the authorization_code grant type
   *
   * <p>For Public Client, e.g. Android apps ,you need to provide the code_verifier used to generate
   * the challenge sent to Auth0 {@literal /authorize} method like:
   *
   * <pre>{@code
   * AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0("clientId", "domain"));
   * client
   *     .token("code", "redirect_uri")
   *     .setCodeVerifier("code_verifier")
   *     .start(new Callback<Credentials> {...});
   * }</pre>
   *
   * For the rest of clients, clients who can safely keep a {@literal client_secret}, you need to
   * provide it instead like:
   *
   * <pre>{@code
   * AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0("clientId", "domain"));
   * client
   *     .token("code", "redirect_uri")
   *     .setClientSecret("client_secret")
   *     .start(new Callback<Credentials> {...});
   * }</pre>
   *
   * @param authorizationCode the authorization code received from the /authorize call.
   * @param redirectUri the uri sent to /authorize as the 'redirect_uri'.
   * @return a request to obtain access_token by exchanging a authorization code.
   */
  @SuppressWarnings("WeakerAccess")
  public TokenRequest token(String authorizationCode, String redirectUri) {
    Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .setClientId(getClientId())
            .setGrantType(GRANT_TYPE_AUTHORIZATION_CODE)
            .set(OAUTH_CODE_KEY, authorizationCode)
            .set(REDIRECT_URI_KEY, redirectUri)
            .asDictionary();

    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(OAUTH_PATH)
            .addPathSegment(TOKEN_PATH)
            .build();

    ParameterizableRequest<Credentials> request =
        factory.POST(url, client, gson, Credentials.class).addParameters(parameters);
    return new TokenRequest(request);
  }
  /**
   * Creates a user in a DB connection using <a
   * href="https://auth0.com/docs/auth-api#!#post--dbconnections-signup">'/dbconnections/signup'
   * endpoint</a> Example usage:
   *
   * <pre><code>
   * client.createUser("{email}", "{password}", "{username}")
   *      .setConnection("{connection name}")
   *      .start(new BaseCallback<DatabaseUser>() {
   *          {@literal}Override
   *          public void onSuccess(DatabaseUser payload) { }
   *
   *          {@literal}@Override
   *          public void onFailure(Auth0Exception error) { }
   *      });
   * </code></pre>
   *
   * @param email of the user and must be non null
   * @param password of the user and must be non null
   * @param username of the user and must be non null
   * @return a request to start
   */
  @SuppressWarnings("WeakerAccess")
  public DatabaseConnectionRequest<DatabaseUser> createUser(
      String email, String password, String username) {
    HttpUrl url =
        HttpUrl.parse(auth0.getDomainUrl())
            .newBuilder()
            .addPathSegment(DB_CONNECTIONS_PATH)
            .addPathSegment(SIGN_UP_PATH)
            .build();

    final Map<String, Object> parameters =
        ParameterBuilder.newBuilder()
            .set(USERNAME_KEY, username)
            .set(EMAIL_KEY, email)
            .set(PASSWORD_KEY, password)
            .setConnection(defaultDatabaseConnection)
            .setClientId(getClientId())
            .asDictionary();
    final ParameterizableRequest<DatabaseUser> request =
        factory.POST(url, client, gson, DatabaseUser.class).addParameters(parameters);
    return new DatabaseConnectionRequest<>(request);
  }