package ch.threema.app.utils;

import org.slf4j.Logger;

import java.time.Instant;

import androidx.annotation.NonNull;
import ch.threema.app.ThreemaApplication;
import static ch.threema.base.utils.LoggingKt.getThreemaLogger;

import ch.threema.app.managers.ServiceManager;
import ch.threema.domain.protocol.api.APIConnector;
import ch.threema.logging.ThreemaLogger;

public class TurnServerCache {
    // Logger
    private final Logger logger = getThreemaLogger("TurnServerCache");

    private final String type;
    private final int minSpareValidity;

    private APIConnector.TurnServerInfo cachedTurnServerInfo;

    /**
     * Create a new TURN server cache of the specified type and with a given minimum validity
     * before the cache is refreshed.
     *
     * @param type             TURN server type, e.g. "voip" or "web"
     * @param minSpareValidity minimum spare validity (in ms)
     */
    public TurnServerCache(@NonNull String type, int minSpareValidity) {
        if (this.logger instanceof ThreemaLogger) {
            ((ThreemaLogger) this.logger).setPrefix("[type=" + type + "]");
        }
        logger.info("Init (type={}, minSpareValidity={})", type, minSpareValidity);
        this.type = type;
        this.minSpareValidity = minSpareValidity;
    }

    /**
     * Start fetching TURN servers asynchronously, so they are more likely to be available
     * without delay when a call is accepted.
     */
    public void prefetchTurnServers() {
        logger.info("prefetchTurnServers");
        new Thread(() -> {
            try {
                getTurnServers();
            } catch (Exception ignored) {
                // ignored
            }
        }).start();
    }

    /**
     * Get TURN servers for use with VoIP. This call may block if no cached information is available.
     *
     * @return TURN server URLs/credentials
     * @throws Exception
     */
    @NonNull
    public synchronized APIConnector.TurnServerInfo getTurnServers() throws Exception {
        if (cachedTurnServerInfo != null) {
            logger.debug("Found cached TURN server info");
            long minExpiration = Instant.now().toEpochMilli() + minSpareValidity;
            if (cachedTurnServerInfo.expirationDate.toEpochMilli() > minExpiration) {
                logger.info("Returning cached TURN server info");
                return cachedTurnServerInfo;
            }
            logger.debug("Cached TURN server info expired");
        }

        logger.info("Returning fresh TURN server info");
        var serviceManager = ServiceManager.require();
        cachedTurnServerInfo = serviceManager
            .getApiConnector()
            .obtainTurnServers(serviceManager.getIdentityStore(), type);

        return cachedTurnServerInfo;
    }
}
