from functools import lru_cache
from hashlib import blake2s

from django.core.cache import cache
from django.core.exceptions import EmptyResultSet
from django.conf import settings
from django.utils.regex_helper import _lazy_re_compile as re_compile

# intuitively named decorator for methods that should only be run once
call_once = lru_cache(maxsize=None)


# ---------------------------------------------------------------------------
def cache_key(value: bytes | str) -> str:
    """
    Return an encoded value of the string suitable
    for using as cache key.
    """
    if isinstance(value, bytes):
        return blake2s(value, digest_size=16).hexdigest()

    return blake2s(value.encode(), digest_size=16).hexdigest()


# ---------------------------------------------------------------------------
def query_cache_key(query: str) -> str:
    """
    Get query cache key
    """
    try:
        return f"filtered-query-count:{cache_key(query)}"
    except (AttributeError, EmptyResultSet):
        return ''


# ---------------------------------------------------------------------------
def get_cache_query_count(key: str) -> int | None:
    """
    Get query count from cache
    """
    if key:
        value = cache.get(key)
        if value is not None:
            return value
    return None


# ---------------------------------------------------------------------------
def set_cache_query_count(key: str, count: int) -> None:
    """
    Add query count result to cache
    """
    cache.set(key, count, settings.DATATABLES_QUERYSET_CACHE_TTL)


__all__ = (
    'call_once',
    're_compile',
    'cache_key',
    'query_cache_key',
    'get_cache_query_count',
    'set_cache_query_count',
)
