import json
import os

from functools import wraps
from typing import Any

from aiocache import Cache, RedisCache
from starlette.requests import Request

from ..util.singleton import SingletonMeta


class CacheService(metaclass=SingletonMeta):

    def __init__(self):
        endpoint = os.environ.get('CACHE_ENDPOINT', 'localhost')
        port = os.environ.get('CACHE_PORT', 6379)

        self._cache = RedisCache(endpoint=endpoint, port=port)

    async def get(self, key: str) -> Any:
        namespace = key.split(":")[0]

        if value := await self._cache.get(key, namespace=namespace):
            return json.loads(value)

        return None

    async def set(self, key, value, ttl=300):
        namespace = key.split(":")[0]

        return await self._cache.set(key, json.dumps(value), ttl=ttl, namespace=namespace)


def cache_response(namespace: str, ttl: int = 300):
    """
    Caching decorator for FastAPI endpoints.

    ttl: Time to live for the cache in seconds.
    namespace: Namespace for cache keys in Redis.
    """
    def decorator(func):

        @wraps(func)
        async def wrapper(*args, **kwargs):
            try:
                cache: CacheService = CacheService()
            except Exception as e:
                print(f"Error creating cache: {e}")
                return await func(*args, **kwargs)

            # Get request object
            request: Request = kwargs.get("request")
            if not request:
                for arg in args:
                    if isinstance(arg, Request):
                        request = arg
                        break


            organization_id = kwargs.get("x_organization_id")
            cache_key = f"{namespace}:org:{organization_id}"

            # Add the route to the cache key
            if request:
                route_path = request.url.path
                cache_key += f":route:{route_path}"

            # Try to retrieve data from cache
            if cached_value := await cache.get(cache_key):
                return cached_value  # Return cached data

            response = await func(*args, **kwargs)

            try:
                # Store the response in Redis with a TTL
                await cache.set(cache_key, response, ttl=ttl)
            except Exception as e:
                print(f"Error caching data: {e}")

            return response

        return wrapper

    return decorator
