package org.thoughtcrime.securesms.notifications;

import android.content.Context;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import com.google.android.gms.tasks.Tasks;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import org.thoughtcrime.securesms.ApplicationContext;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.service.FetchForegroundService;
import org.thoughtcrime.securesms.util.Util;

public class FcmReceiveService extends FirebaseMessagingService {
  private static final String TAG = FcmReceiveService.class.getSimpleName();
  private static final Object INIT_LOCK = new Object();
  private static boolean initialized;
  private static volatile boolean triedRegistering;
  private static volatile String prefixedToken;

  public static void register(Context context) {

    if (FcmReceiveService.prefixedToken != null) {
      Log.i(TAG, "FCM already registered");
      triedRegistering = true;
      return;
    }

    Util.runOnAnyBackgroundThread(() -> {
      final String rawToken;

      try {
        synchronized (INIT_LOCK) {
          if (!initialized) {
            // manual init: read tokens from `./google-services.json`;
            // automatic init disabled in AndroidManifest.xml to skip FCM code completely.
            FirebaseApp.initializeApp(context);
          }
          initialized = true;
        }
        rawToken = Tasks.await(FirebaseMessaging.getInstance().getToken());
      } catch (Exception e) {
        // we're here usually when FCM is not available and initializeApp() or getToken() failed.
        Log.w(TAG, "cannot get FCM token for " + BuildConfig.APPLICATION_ID + ": " + e);
        triedRegistering = true;
        return;
      }
      if (TextUtils.isEmpty(rawToken)) {
        Log.w(TAG, "got empty FCM token for " + BuildConfig.APPLICATION_ID);
        triedRegistering = true;
        return;
      }

      prefixedToken = addPrefix(rawToken);
      Log.i(TAG, "FCM token: " + prefixedToken);
      ApplicationContext.dcAccounts.setPushDeviceToken(prefixedToken);
      triedRegistering = true;
    });
  }

  // wait a until FCM registration got a token or not.
  // we're calling register() pretty soon and getToken() pretty late on init,
  // so usually, this should not block anything.
  // still, waitForRegisterFinished() needs to be called from a background thread.
  @WorkerThread
  public static void waitForRegisterFinished() {
    while (!triedRegistering) {
      Util.sleep(100);
    }
  }

  private static String addPrefix(String rawToken) {
    return "fcm-" + BuildConfig.APPLICATION_ID + ":" + rawToken;
  }

  @Nullable
  public static String getToken() {
    return prefixedToken;
  }

  @WorkerThread
  @Override
  public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
    Log.i(TAG, "FCM push notification received");
    FetchForegroundService.start(this);
  }

  @Override
  public void onDeletedMessages() {
    Log.i(TAG, "FCM push notifications dropped");
    FetchForegroundService.start(this);
  }

  @Override
  public void onNewToken(@NonNull String rawToken) {
    prefixedToken = addPrefix(rawToken);
    Log.i(TAG, "new FCM token: " + prefixedToken);
    ApplicationContext.dcAccounts.setPushDeviceToken(prefixedToken);
  }
}
