package dev.elaine.redislab.cache;

import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.function.IntSupplier;

public final class CacheAsideService {
  public static final String NULL_SENTINEL = "<NULL>";

  private final Repository repository;
  private final Cache cache;
  private final IntSupplier jitterSeconds;

  public CacheAsideService(Repository repository, Cache cache, IntSupplier jitterSeconds) {
    this.repository = Objects.requireNonNull(repository);
    this.cache = Objects.requireNonNull(cache);
    this.jitterSeconds = Objects.requireNonNull(jitterSeconds);
  }

  public Optional<String> findProduct(long id) {
    String key = "product:" + id;
    Optional<String> cached = cache.get(key);
    if (cached.isPresent()) {
      return NULL_SENTINEL.equals(cached.get()) ? Optional.empty() : cached;
    }

    Optional<String> loaded = repository.findProduct(id);
    Duration ttl = loaded.isPresent()
        ? Duration.ofSeconds(300L + jitterSeconds.getAsInt())
        : Duration.ofSeconds(30);
    cache.put(key, loaded.orElse(NULL_SENTINEL), ttl);
    return loaded;
  }

  public void updateProduct(long id, String value) {
    repository.updateProduct(id, value);
    cache.delete("product:" + id);
  }

  public interface Repository {
    Optional<String> findProduct(long id);
    void updateProduct(long id, String value);
  }

  public interface Cache {
    Optional<String> get(String key);
    void put(String key, String value, Duration ttl);
    void delete(String key);
  }
}
