from redis import Redis
import json
import logging
import google.cloud.logging
from typing import Callable

## Setup Logger
client = google.cloud.logging.Client()
client.setup_logging()
logger = logging.getLogger("uvicorn")
logger.setLevel(logging.INFO)


class RedisCache:
  redis_conn: Redis | None = None

  def init_cache(self, host, port):
    self.redis_conn = Redis(
        host=host,
        port=port,
        decode_responses=True,
        socket_connect_timeout=5
    )

  def get_redis_instance(self):
    return self.redis_conn

  def compute_cache_aside(
      self, 
      key: str, 
      compute_fn: Callable[[], any],
      ex: int,
      use_cache: bool = True
  ) -> any:
    """
    Cache-aside pattern: attempts to retrieve a value from Redis by key.
    On a cache hit, returns the cached value. On a miss, calls compute_fn
    to produce the result, stores it in Redis with the given TTL, and returns it.

    If the Redis connection was not initialized (redis_conn is None/unset),
    the cache is skipped entirely — compute_fn is called directly and its
    result is returned without any caching. This allows callers to use this
    method safely even before init_cache has been called.

    Args:
        key: The Redis key to look up and store the result under.
        compute_fn: A callable (takes no arguments) that produces the value
                    to cache. Only invoked on a cache miss.
        ex: Expiration time in seconds for the cached entry.
        use_cache: Whether to use the cache. Defaults to True but is not enforced —
                    callers can pass False to bypass the cache and always invoke compute_fn.

    Returns:
        The cached value (on hit) or the freshly computed value (on miss).
    """
    if use_cache and self.redis_conn:
        cached_value = self.redis_conn.get(key)
        if cached_value:
            logger.info(f"Cache HIT for key: {key}")
            return json.loads(cached_value)

    result = compute_fn()

    if use_cache and self.redis_conn and result:
        self.redis_conn.set(key, json.dumps(result), ex=ex)
        logger.info(f"Cached result for key: {key} (TTL={ex}s)")

    return result


redis_instance: RedisCache = RedisCache()