/*
 * Copyright 2018 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.firebase.snippets;

import com.google.common.io.BaseEncoding;
import com.google.firebase.auth.ActionCodeSettings;
import com.google.firebase.auth.ErrorInfo;
import com.google.firebase.auth.ExportedUserRecord;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseToken;
import com.google.firebase.auth.ImportUserRecord;
import com.google.firebase.auth.ListUsersPage;
import com.google.firebase.auth.SessionCookieOptions;
import com.google.firebase.auth.UserImportOptions;
import com.google.firebase.auth.UserImportResult;
import com.google.firebase.auth.UserProvider;
import com.google.firebase.auth.UserRecord;
import com.google.firebase.auth.UserRecord.CreateRequest;
import com.google.firebase.auth.UserRecord.UpdateRequest;
import com.google.firebase.auth.hash.Bcrypt;
import com.google.firebase.auth.hash.HmacSha256;
import com.google.firebase.auth.hash.Pbkdf2Sha256;
import com.google.firebase.auth.hash.Scrypt;
import com.google.firebase.auth.hash.StandardScrypt;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;

/**
 * Auth snippets for documentation.
 */
public class FirebaseAuthSnippets {

  public static void getUserById(String uid) throws FirebaseAuthException {
    // [START get_user_by_id]
    UserRecord userRecord = FirebaseAuth.getInstance().getUser(uid);
    // See the UserRecord reference doc for the contents of userRecord.
    System.out.println("Successfully fetched user data: " + userRecord.getUid());
    // [END get_user_by_id]
  }

  public static void getUserByEmail(String email) throws FirebaseAuthException {
    // [START get_user_by_email]
    UserRecord userRecord = FirebaseAuth.getInstance().getUserByEmail(email);
    // See the UserRecord reference doc for the contents of userRecord.
    System.out.println("Successfully fetched user data: " + userRecord.getEmail());
    // [END get_user_by_email]
  }

  public static void getUserByPhoneNumber(
      String phoneNumber) throws FirebaseAuthException {
    // [START get_user_by_phone]
    UserRecord userRecord = FirebaseAuth.getInstance().getUserByPhoneNumber(phoneNumber);
    // See the UserRecord reference doc for the contents of userRecord.
    System.out.println("Successfully fetched user data: " + userRecord.getPhoneNumber());
    // [END get_user_by_phone]
  }

  public static void createUser() throws FirebaseAuthException {
    // [START create_user]
    CreateRequest request = new CreateRequest()
        .setEmail("[email protected]")
        .setEmailVerified(false)
        .setPassword("secretPassword")
        .setPhoneNumber("+11234567890")
        .setDisplayName("John Doe")
        .setPhotoUrl("http://www.example.com/12345678/photo.png")
        .setDisabled(false);

    UserRecord userRecord = FirebaseAuth.getInstance().createUser(request);
    System.out.println("Successfully created new user: " + userRecord.getUid());
    // [END create_user]
  }

  public static void createUserWithUid() throws FirebaseAuthException {
    // [START create_user_with_uid]
    CreateRequest request = new CreateRequest()
        .setUid("some-uid")
        .setEmail("[email protected]")
        .setPhoneNumber("+11234567890");

    UserRecord userRecord = FirebaseAuth.getInstance().createUser(request);
    System.out.println("Successfully created new user: " + userRecord.getUid());
    // [END create_user_with_uid]
  }

  public static void updateUser(String uid) throws FirebaseAuthException {
    // [START update_user]
    UpdateRequest request = new UpdateRequest(uid)
        .setEmail("[email protected]")
        .setPhoneNumber("+11234567890")
        .setEmailVerified(true)
        .setPassword("newPassword")
        .setDisplayName("Jane Doe")
        .setPhotoUrl("http://www.example.com/12345678/photo.png")
        .setDisabled(true);

    UserRecord userRecord = FirebaseAuth.getInstance().updateUser(request);
    System.out.println("Successfully updated user: " + userRecord.getUid());
    // [END update_user]
  }

  public static void setCustomUserClaims(
      String uid) throws FirebaseAuthException {
    // [START set_custom_user_claims]
    // Set admin privilege on the user corresponding to uid.
    Map<String, Object> claims = new HashMap<>();
    claims.put("admin", true);
    FirebaseAuth.getInstance().setCustomUserClaims(uid, claims);
    // The new custom claims will propagate to the user's ID token the
    // next time a new one is issued.
    // [END set_custom_user_claims]

    String idToken = "id_token";
    // [START verify_custom_claims]
    // Verify the ID token first.
    FirebaseToken decoded = FirebaseAuth.getInstance().verifyIdToken(idToken);
    if (Boolean.TRUE.equals(decoded.getClaims().get("admin"))) {
      // Allow access to requested admin resource.
    }
    // [END verify_custom_claims]

    // [START read_custom_user_claims]
    // Lookup the user associated with the specified uid.
    UserRecord user = FirebaseAuth.getInstance().getUser(uid);
    System.out.println(user.getCustomClaims().get("admin"));
    // [END read_custom_user_claims]
  }

  public static void setCustomUserClaimsScript() throws FirebaseAuthException {
    // [START set_custom_user_claims_script]
    UserRecord user = FirebaseAuth.getInstance()
        .getUserByEmail("[email protected]");
    // Confirm user is verified.
    if (user.isEmailVerified()) {
      Map<String, Object> claims = new HashMap<>();
      claims.put("admin", true);
      FirebaseAuth.getInstance().setCustomUserClaims(user.getUid(), claims);
    }
    // [END set_custom_user_claims_script]
  }

  public static void setCustomUserClaimsInc() throws FirebaseAuthException {
    // [START set_custom_user_claims_incremental]
    UserRecord user = FirebaseAuth.getInstance()
        .getUserByEmail("[email protected]");
    // Add incremental custom claim without overwriting the existing claims.
    Map<String, Object> currentClaims = user.getCustomClaims();
    if (Boolean.TRUE.equals(currentClaims.get("admin"))) {
      // Add level.
      currentClaims.put("level", 10);
      // Add custom claims for additional privileges.
      FirebaseAuth.getInstance().setCustomUserClaims(user.getUid(), currentClaims);
    }
    // [END set_custom_user_claims_incremental]
  }

  public static void listAllUsers() throws FirebaseAuthException {
    // [START list_all_users]
    // Start listing users from the beginning, 1000 at a time.
    ListUsersPage page = FirebaseAuth.getInstance().listUsers(null);
    while (page != null) {
      for (ExportedUserRecord user : page.getValues()) {
        System.out.println("User: " + user.getUid());
      }
      page = page.getNextPage();
    }

    // Iterate through all users. This will still retrieve users in batches,
    // buffering no more than 1000 users in memory at a time.
    page = FirebaseAuth.getInstance().listUsers(null);
    for (ExportedUserRecord user : page.iterateAll()) {
      System.out.println("User: " + user.getUid());
    }
    // [END list_all_users]
  }

  public static void deleteUser(String uid) throws FirebaseAuthException {
    // [START delete_user]
    FirebaseAuth.getInstance().deleteUser(uid);
    System.out.println("Successfully deleted user.");
    // [END delete_user]
  }

  public static void createCustomToken() throws FirebaseAuthException {
    // [START custom_token]
    String uid = "some-uid";

    String customToken = FirebaseAuth.getInstance().createCustomToken(uid);
    // Send token back to client
    // [END custom_token]
    System.out.println("Created custom token: " + customToken);
  }

  public static void createCustomTokenWithClaims() throws FirebaseAuthException {
    // [START custom_token_with_claims]
    String uid = "some-uid";
    Map<String, Object> additionalClaims = new HashMap<String, Object>();
    additionalClaims.put("premiumAccount", true);

    String customToken = FirebaseAuth.getInstance()
        .createCustomToken(uid, additionalClaims);
    // Send token back to client
    // [END custom_token_with_claims]
    System.out.println("Created custom token: " + customToken);
  }

  public static void verifyIdToken(String idToken) throws FirebaseAuthException {
    // [START verify_id_token]
    // idToken comes from the client app (shown above)
    FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
    String uid = decodedToken.getUid();
    // [END verify_id_token]
    System.out.println("Decoded ID token from user: " + uid);
  }

  public static void verifyIdTokenCheckRevoked(String idToken) {
    // [START verify_id_token_check_revoked]
    try {
      // Verify the ID token while checking if the token is revoked by passing checkRevoked
      // as true.
      boolean checkRevoked = true;
      FirebaseToken decodedToken = FirebaseAuth.getInstance()
          .verifyIdToken(idToken, checkRevoked);
      // Token is valid and not revoked.
      String uid = decodedToken.getUid();
    } catch (FirebaseAuthException e) {
      if (e.getErrorCode().equals("id-token-revoked")) {
        // Token has been revoked. Inform the user to re-authenticate or signOut() the user.
      } else {
        // Token is invalid.
      }
    }
    // [END verify_id_token_check_revoked]
  }

  public static void revokeIdTokens(
      String idToken) throws FirebaseAuthException {
    String uid = "someUid";
    // [START revoke_tokens]
    FirebaseAuth.getInstance().revokeRefreshTokens(uid);
    UserRecord user = FirebaseAuth.getInstance().getUser(uid);
    // Convert to seconds as the auth_time in the token claims is in seconds too.
    long revocationSecond = user.getTokensValidAfterTimestamp() / 1000;
    System.out.println("Tokens revoked at: " + revocationSecond);
    // [END revoke_tokens]

    // [START save_revocation_in_db]
    DatabaseReference ref = FirebaseDatabase.getInstance().getReference("metadata/" + uid);
    Map<String, Object> userData = new HashMap<>();
    userData.put("revokeTime", revocationSecond);
    ref.setValueAsync(userData);
    // [END save_revocation_in_db]
  }

  static class LoginRequest {
    String idToken;

    String getIdToken() {
      return idToken;
    }
  }

  // [START session_login]
  @POST
  @Path("/sessionLogin")
  @Consumes("application/json")
  public Response createSessionCookie(LoginRequest request) {
    // Get the ID token sent by the client
    String idToken = request.getIdToken();
    // Set session expiration to 5 days.
    long expiresIn = TimeUnit.DAYS.toMillis(5);
    SessionCookieOptions options = SessionCookieOptions.builder()
        .setExpiresIn(expiresIn)
        .build();
    try {
      // Create the session cookie. This will also verify the ID token in the process.
      // The session cookie will have the same claims as the ID token.
      String sessionCookie = FirebaseAuth.getInstance().createSessionCookie(idToken, options);
      // Set cookie policy parameters as required.
      NewCookie cookie = new NewCookie("session", sessionCookie /* ... other parameters */);
      return Response.ok().cookie(cookie).build();
    } catch (FirebaseAuthException e) {
      return Response.status(Status.UNAUTHORIZED).entity("Failed to create a session cookie")
          .build();
    }
  }
  // [END session_login]

  public Response checkAuthTime(String idToken) throws FirebaseAuthException {
    // [START check_auth_time]
    // To ensure that cookies are set only on recently signed in users, check auth_time in
    // ID token before creating a cookie.
    FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(idToken);
    long authTimeMillis = TimeUnit.SECONDS.toMillis(
        (long) decodedToken.getClaims().get("auth_time"));

    // Only process if the user signed in within the last 5 minutes.
    if (System.currentTimeMillis() - authTimeMillis < TimeUnit.MINUTES.toMillis(5)) {
      long expiresIn = TimeUnit.DAYS.toMillis(5);
      SessionCookieOptions options = SessionCookieOptions.builder()
          .setExpiresIn(expiresIn)
          .build();
      String sessionCookie = FirebaseAuth.getInstance().createSessionCookie(idToken, options);
      // Set cookie policy parameters as required.
      NewCookie cookie = new NewCookie("session", sessionCookie);
      return Response.ok().cookie(cookie).build();
    }
    // User did not sign in recently. To guard against ID token theft, require
    // re-authentication.
    return Response.status(Status.UNAUTHORIZED).entity("Recent sign in required").build();
    // [END check_auth_time]
  }

  private Response serveContentForUser(FirebaseToken decodedToken) {
    return null;
  }

  private Response serveContentForAdmin(FirebaseToken decodedToken) {
    return null;
  }

  // [START session_verify]
  @POST
  @Path("/profile")
  public Response verifySessionCookie(@CookieParam("session") Cookie cookie) {
    String sessionCookie = cookie.getValue();
    try {
      // Verify the session cookie. In this case an additional check is added to detect
      // if the user's Firebase session was revoked, user deleted/disabled, etc.
      final boolean checkRevoked = true;
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(
          sessionCookie, checkRevoked);
      return serveContentForUser(decodedToken);
    } catch (FirebaseAuthException e) {
      // Session cookie is unavailable, invalid or revoked. Force user to login.
      return Response.temporaryRedirect(URI.create("/login")).build();
    }
  }
  // [END session_verify]

  public Response checkPermissions(String sessionCookie) {
    // [START session_verify_with_permission_check]
    try {
      final boolean checkRevoked = true;
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(
          sessionCookie, checkRevoked);
      if (Boolean.TRUE.equals(decodedToken.getClaims().get("admin"))) {
        return serveContentForAdmin(decodedToken);
      }
      return Response.status(Status.UNAUTHORIZED).entity("Insufficient permissions").build();
    } catch (FirebaseAuthException e) {
      // Session cookie is unavailable, invalid or revoked. Force user to login.
      return Response.temporaryRedirect(URI.create("/login")).build();
    }
    // [END session_verify_with_permission_check]
  }

  // [START session_clear]
  @POST
  @Path("/sessionLogout")
  public Response clearSessionCookie(@CookieParam("session") Cookie cookie) {
    final int maxAge = 0;
    NewCookie newCookie = new NewCookie(cookie, null, maxAge, true);
    return Response.temporaryRedirect(URI.create("/login")).cookie(newCookie).build();
  }
  // [END session_clear]

  // [START session_clear_and_revoke]
  @POST
  @Path("/sessionLogout")
  public Response clearSessionCookieAndRevoke(@CookieParam("session") Cookie cookie) {
    String sessionCookie = cookie.getValue();
    try {
      FirebaseToken decodedToken = FirebaseAuth.getInstance().verifySessionCookie(sessionCookie);
      FirebaseAuth.getInstance().revokeRefreshTokens(decodedToken.getUid());
      final int maxAge = 0;
      NewCookie newCookie = new NewCookie(cookie, null, maxAge, true);
      return Response.temporaryRedirect(URI.create("/login")).cookie(newCookie).build();
    } catch (FirebaseAuthException e) {
      return Response.temporaryRedirect(URI.create("/login")).build();
    }
  }
  // [END session_clear_and_revoke]

  public void importUsers() {
    // [START build_user_list]
    // Up to 1000 users can be imported at once.
    List<ImportUserRecord> users = new ArrayList<>();
    users.add(ImportUserRecord.builder()
        .setUid("uid1")
        .setEmail("[email protected]")
        .setPasswordHash("passwordHash1".getBytes())
        .setPasswordSalt("salt1".getBytes())
        .build());
    users.add(ImportUserRecord.builder()
        .setUid("uid2")
        .setEmail("[email protected]")
        .setPasswordHash("passwordHash2".getBytes())
        .setPasswordSalt("salt2".getBytes())
        .build());
    // [END build_user_list]

    // [START import_users]
    UserImportOptions options = UserImportOptions.withHash(
        HmacSha256.builder()
            .setKey("secretKey".getBytes())
            .build());
    try {
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      System.out.println("Successfully imported " + result.getSuccessCount() + " users");
      System.out.println("Failed to import " + result.getFailureCount() + " users");
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user at index: " + indexedError.getIndex()
            + " due to error: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      // Some unrecoverable error occurred that prevented the operation from running.
    }
    // [END import_users]
  }

  public void importWithHmac() {
    // [START import_with_hmac]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setEmail("[email protected]")
          .setPasswordHash("password-hash".getBytes())
          .setPasswordSalt("salt".getBytes())
          .build());
      UserImportOptions options = UserImportOptions.withHash(
          HmacSha256.builder()
              .setKey("secret".getBytes())
              .build());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_with_hmac]
  }

  public void importWithPbkdf() {
    // [START import_with_pbkdf]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setEmail("[email protected]")
          .setPasswordHash("password-hash".getBytes())
          .setPasswordSalt("salt".getBytes())
          .build());
      UserImportOptions options = UserImportOptions.withHash(
          Pbkdf2Sha256.builder()
              .setRounds(100000)
              .build());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_with_pbkdf]
  }

  public void importWithStandardScrypt() {
    // [START import_with_standard_scrypt]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setEmail("[email protected]")
          .setPasswordHash("password-hash".getBytes())
          .setPasswordSalt("salt".getBytes())
          .build());
      UserImportOptions options = UserImportOptions.withHash(
          StandardScrypt.builder()
              .setMemoryCost(1024)
              .setParallelization(16)
              .setBlockSize(8)
              .setDerivedKeyLength(64)
              .build());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_with_standard_scrypt]
  }

  public void importWithBcrypt() {
    // [START import_with_bcrypt]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setEmail("[email protected]")
          .setPasswordHash("password-hash".getBytes())
          .setPasswordSalt("salt".getBytes())
          .build());
      UserImportOptions options = UserImportOptions.withHash(Bcrypt.getInstance());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_with_bcrypt]
  }

  public void importWithScrypt() {
    // [START import_with_scrypt]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setEmail("[email protected]")
          .setPasswordHash("password-hash".getBytes())
          .setPasswordSalt("salt".getBytes())
          .build());
      UserImportOptions options = UserImportOptions.withHash(
          Scrypt.builder()
              // All the parameters below can be obtained from the Firebase Console's "Users"
              // section. Base64 encoded parameters must be decoded into raw bytes.
              .setKey(BaseEncoding.base64().decode("base64-secret"))
              .setSaltSeparator(BaseEncoding.base64().decode("base64-salt-separator"))
              .setRounds(8)
              .setMemoryCost(14)
              .build());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users, options);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_with_scrypt]
  }

  public void importWithoutPassword() {
    // [START import_without_password]
    try {
      List<ImportUserRecord> users = Collections.singletonList(ImportUserRecord.builder()
          .setUid("some-uid")
          .setDisplayName("John Doe")
          .setEmail("[email protected]")
          .setPhotoUrl("http://www.example.com/12345678/photo.png")
          .setEmailVerified(true)
          .setPhoneNumber("+11234567890")
          .putCustomClaim("admin", true) // set this user as admin
          .addUserProvider(UserProvider.builder() // user with Google provider
              .setUid("google-uid")
              .setEmail("[email protected]")
              .setDisplayName("John Doe")
              .setPhotoUrl("http://www.example.com/12345678/photo.png")
              .setProviderId("google.com")
              .build())
          .build());
      UserImportResult result = FirebaseAuth.getInstance().importUsers(users);
      for (ErrorInfo indexedError : result.getErrors()) {
        System.out.println("Failed to import user: " + indexedError.getReason());
      }
    } catch (FirebaseAuthException e) {
      System.out.println("Error importing users: " + e.getMessage());
    }
    // [END import_without_password]
  }

  public ActionCodeSettings initActionCodeSettings() {
    // [START init_action_code_settings]
    ActionCodeSettings actionCodeSettings = ActionCodeSettings.builder()
        .setUrl("https://www.example.com/checkout?cartId=1234")
        .setHandleCodeInApp(true)
        .setIosBundleId("com.example.ios")
        .setAndroidPackageName("com.example.android")
        .setAndroidInstallApp(true)
        .setAndroidMinimumVersion("12")
        .setDynamicLinkDomain("coolapp.page.link")
        .build();
    // [END init_action_code_settings]
    return actionCodeSettings;
  }

  public void generatePasswordResetLink() {
    final ActionCodeSettings actionCodeSettings = initActionCodeSettings();
    final String displayName = "Example User";
    // [START password_reset_link]
    String email = "[email protected]";
    try {
      String link = FirebaseAuth.getInstance().generatePasswordResetLink(
          email, actionCodeSettings);
      // Construct email verification template, embed the link and send
      // using custom SMTP server.
      sendCustomPasswordResetEmail(email, displayName, link);
    } catch (FirebaseAuthException e) {
      System.out.println("Error generating email link: " + e.getMessage());
    }
    // [END password_reset_link]
  }

  public void generateEmailVerificationLink() {
    final ActionCodeSettings actionCodeSettings = initActionCodeSettings();
    final String displayName = "Example User";
    // [START email_verification_link]
    String email = "[email protected]";
    try {
      String link = FirebaseAuth.getInstance().generateEmailVerificationLink(
          email, actionCodeSettings);
      // Construct email verification template, embed the link and send
      // using custom SMTP server.
      sendCustomPasswordResetEmail(email, displayName, link);
    } catch (FirebaseAuthException e) {
      System.out.println("Error generating email link: " + e.getMessage());
    }
    // [END email_verification_link]
  }

  public void generateSignInWithEmailLink() {
    final ActionCodeSettings actionCodeSettings = initActionCodeSettings();
    final String displayName = "Example User";
    // [START sign_in_with_email_link]
    String email = "[email protected]";
    try {
      String link = FirebaseAuth.getInstance().generateSignInWithEmailLink(
          email, actionCodeSettings);
      // Construct email verification template, embed the link and send
      // using custom SMTP server.
      sendCustomPasswordResetEmail(email, displayName, link);
    } catch (FirebaseAuthException e) {
      System.out.println("Error generating email link: " + e.getMessage());
    }
    // [END sign_in_with_email_link]
  }

  // Place holder method to make the compiler happy. This is referenced by all email action
  // link snippets.
  private void sendCustomPasswordResetEmail(String email, String displayName, String link) {}
}