package ch.threema.app.push;

import android.content.Context;
import android.text.format.DateUtils;

import androidx.annotation.NonNull;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.tasks.Tasks;
import com.google.firebase.installations.FirebaseInstallations;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

import org.slf4j.Logger;

import java.time.Instant;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import ch.threema.app.utils.PushUtil;
import ch.threema.app.utils.RuntimeUtil;
import ch.threema.base.ThreemaException;
import static ch.threema.base.utils.LoggingKt.getThreemaLogger;
import static ch.threema.base.TestUtilKt.isInDeviceTest;

import ch.threema.domain.protocol.csp.ProtocolDefines;

public class PushService extends FirebaseMessagingService {
    private static final Logger logger = getThreemaLogger("PushService");

    public static final String EXTRA_CLEAR_TOKEN = "clear";
    public static final String EXTRA_WITH_CALLBACK = "cb";

    @Override
    public void onNewToken(@NonNull String token) {
        logger.info("New FCM token received");
        logger.debug("FCM push token: {}", token);
        try {
            PushUtil.sendTokenToServer(token, ProtocolDefines.PUSHTOKEN_TYPE_FCM);
        } catch (ThreemaException e) {
            logger.error("onNewToken, could not send token to server ", e);
        }
    }

    @SuppressWarnings("unused")
    public static String deleteToken(Context context) {
        try {
            FirebaseMessaging.getInstance().deleteToken();
            Tasks.await(FirebaseInstallations.getInstance().delete());
            PushUtil.sendTokenToServer("", ProtocolDefines.PUSHTOKEN_TYPE_NONE);
        } catch (ThreemaException | ExecutionException | InterruptedException e) {
            logger.warn("Could not delete FCM token", e);
            return e.getMessage();
        }
        return null;
    }

    @Override
    public void onDeletedMessages() {
        logger.info("Too many messages stored on the Firebase server. Messages have been dropped.");
    }

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        logger.info("Handling incoming FCM intent.");

        RuntimeUtil.runInWakelock(getApplicationContext(), DateUtils.MINUTE_IN_MILLIS * 10, "PushService", () -> processFcmMessage(remoteMessage));
    }

    private void processFcmMessage(RemoteMessage remoteMessage) {
        logger.info("Received FCM message: {}", remoteMessage.getMessageId());

        // Log message sent time
        try {
            Instant sentDate = Instant.ofEpochMilli(remoteMessage.getSentTime());
            Instant receivedDate = Instant.now();

            logger.info("*** Message sent     : {}", sentDate);
            logger.info("*** Message received : {}", receivedDate);
            logger.info("*** Original priority: {}", remoteMessage.getOriginalPriority());
            logger.info("*** Current priority : {}", remoteMessage.getPriority());
        } catch (Exception ignore) {
        }

        Map<String, String> data = remoteMessage.getData();
        PushUtil.processRemoteMessage(data);
    }

    // following services check are handled here and not in ConfigUtils to minimize number of duplicating classes

    /**
     * check for specific huawei services
     */
    @SuppressWarnings("unused")
    public static boolean hmsServicesInstalled(Context context) {
        return false;
    }

    /**
     * check for specific google services
     */
    public static boolean playServicesInstalled(Context context) {
        GoogleApiAvailability apiAvailability = com.google.android.gms.common.GoogleApiAvailability.getInstance();
        int resultCode = apiAvailability.isGooglePlayServicesAvailable(context);
        return isInDeviceTest() || (resultCode == ConnectionResult.SUCCESS);
    }

    /**
     * check for available push service
     */
    public static boolean servicesInstalled(Context context) {
        return playServicesInstalled(context) || hmsServicesInstalled(context);
    }
}
