package ru.yandex.qe.dispenser.ws.iam;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import ru.yandex.monlib.metrics.labels.Labels;
import ru.yandex.monlib.metrics.registry.MetricRegistry;
import ru.yandex.qe.dispenser.solomon.SolomonHolder;

import javax.inject.Inject;
import java.time.Instant;
import java.util.concurrent.ExecutionException;

@Component
@Profile("billing-integration")
public class IAMTokenSupplier {

    private static final Logger logger = LoggerFactory.getLogger(IAMTokenSupplier.class);

    private static final String TOKEN_KEY = "IAM_TOKEN";

    private final LoadingCache<String, String> tokenCache;

    private volatile long lastSuccess;

    @Inject
    public IAMTokenSupplier(IAMApiHelper iamApiHelper,
                            SolomonHolder solomonHolder,
                            @Value("${iam.oauth.token}") String iamOAuthToken) {
        lastSuccess = Instant.now().toEpochMilli();
        MetricRegistry rootRegistry = solomonHolder.getRootRegistry();
        rootRegistry.lazyGaugeInt64("iam_token_refresh.time_since_last_success_millis",
                Labels.of(), () -> Instant.now().toEpochMilli() - lastSuccess);
        this.tokenCache = CacheBuilder.newBuilder()
                .build(new CacheLoader<>() {
                    @Override
                    public String load(String key) {
                        try {
                            logger.info("Refreshing IAM token...");
                            String token = iamApiHelper.getToken(new IAMTokenRequest(iamOAuthToken)).getIamToken()
                                    .orElseThrow(() -> new IllegalStateException("No IAM token in response"));
                            lastSuccess = Instant.now().toEpochMilli();
                            logger.info("IAM token refreshed");
                            return token;
                        } catch (Exception e) {
                            logger.error("Failed to refresh IAM token", e);
                            throw e;
                        }
                    }
                });
    }

    public String getToken() {
        try {
            return tokenCache.get(TOKEN_KEY);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
    }

    public void refresh() {
        tokenCache.refresh(TOKEN_KEY);
    }
}
