package ch.threema.app.services;

import org.slf4j.Logger;

import java.util.HashMap;
import java.util.concurrent.Future;

import ch.threema.app.ThreemaApplication;
import ch.threema.app.managers.ServiceManager;
import ch.threema.app.utils.ConfigUtils;
import ch.threema.app.utils.ExponentialBackOffUtil;

import static ch.threema.base.utils.LoggingKt.getThreemaLogger;
import static ch.threema.common.OkHttpExtensionsKt.isHttpAuthError;

public class MessageSendingServiceExponentialBackOff implements MessageSendingService {
    private static final Logger logger = getThreemaLogger("MessageSendingServiceExponentialBackOff");

    private final MessageSendingServiceState messageSendingServiceState;
    private final HashMap<String, Future<?>> backoffFutures = new HashMap<>();

    public MessageSendingServiceExponentialBackOff(MessageSendingServiceState messageSendingServiceState) {
        this.messageSendingServiceState = messageSendingServiceState;
    }

    @Override
    public void addToQueue(final MessageSendingProcess process) {
        logger.debug("{} Add message to queue", process.getMessageModel().getUid());
        Future<?> backoffFuture = ExponentialBackOffUtil.getInstance().run(new ExponentialBackOffUtil.BackOffRunnable() {
            @Override
            public void run(int currentRetry) throws Exception {
                try {
                    process.send();
                } catch (Exception e) {
                    logger.error("Sending message failed", e);
                    messageSendingServiceState.exception(e, 0);
                    if (isHttpAuthError(e) && ConfigUtils.isOnPremBuild()) {
                        ServiceManager serviceManager = ServiceManager.get();
                        if (serviceManager != null) {
                            serviceManager.getApiService().invalidateAuthToken();
                        }
                    }
                    throw e;
                }
            }

            @Override
            public void finished(int currentRetry) {
                synchronized (backoffFutures) {
                    backoffFutures.remove(process.getMessageModel().getUid());
                }
                logger.debug("{} Exponential backoff finished successfully", process.getMessageModel().getUid());
            }

            @Override
            public void failed() {
                synchronized (backoffFutures) {
                    backoffFutures.remove(process.getMessageModel().getUid());
                }
                logger.debug("{} Exponential backoff failed", process.getMessageModel().getUid());

                messageSendingServiceState.processingFailed(process.getMessageModel(), process.getReceiver());
            }
        }, 5, process.getMessageModel().getUid());

        if (backoffFuture != null) {
            synchronized (backoffFutures) {
                backoffFutures.put(process.getMessageModel().getUid(), backoffFuture);
            }
        }
    }

    @Override
    public void abort(String messageUid) {
        synchronized (backoffFutures) {
            Future<?> backoffFuture = backoffFutures.get(messageUid);
            if (backoffFuture != null) {
                if (!backoffFuture.isCancelled() && !backoffFuture.isDone()) {
                    logger.debug("{} Cancelling backoff", messageUid);
                    backoffFuture.cancel(true);
                }
                backoffFutures.remove(messageUid);
            }
        }
    }
}
