from collections.abc import Callable, Coroutine
from functools import wraps
from typing import Any, TypeVar

from fastapi.responses import ORJSONResponse
from orjson import dumps, loads
from redis.asyncio import Redis

from apps.users.models import User
from core.config import settings

client = Redis.from_url(settings.REDIS_URL)
T = TypeVar('T')


def cache_response(ttl: int = 60, namespace: str = 'main'):
    def decorator(
        func: Callable[..., Coroutine[Any, Any, T]],
    ) -> Callable[..., Coroutine[Any, Any, T | ORJSONResponse]]:
        @wraps(func)
        async def wrapper(*args: Any, **kwargs: Any):
            cache_key = f'endpoints:{namespace}_{func.__name__}'
            user = kwargs.get('user')
            user_id = kwargs.get('user_id')

            if user_id:
                cache_key += f':{user_id}'

            if isinstance(user, User):
                cache_key += f':{user.id}'

            cached_value = await client.get(cache_key)
            if cached_value:
                return ORJSONResponse(
                    content=loads(cached_value), headers={'X-Cache': 'hit'}
                )

            response = await func(*args, **kwargs)
            await client.set(cache_key, dumps(response), ex=ttl)

            return response

        return wrapper

    return decorator
