"""Query result caching with LRU and TTL support."""

from __future__ import annotations

import functools
import hashlib
import threading
import time
from typing import Any, Callable


class TTLCache:
    """Simple thread-safe TTL cache for search results."""

    def __init__(self, maxsize: int = 256, ttl_seconds: float = 300.0) -> None:
        self._cache: dict[str, tuple[float, Any]] = {}
        self._maxsize = maxsize
        self._ttl = ttl_seconds
        self._lock = threading.Lock()

    def get(self, key: str) -> Any | None:
        with self._lock:
            entry = self._cache.get(key)
            if entry is None:
                return None
            expires, value = entry
            if time.monotonic() > expires:
                del self._cache[key]
                return None
            return value

    def put(self, key: str, value: Any) -> None:
        with self._lock:
            # Evict oldest if at capacity
            if len(self._cache) >= self._maxsize and key not in self._cache:
                oldest_key = min(self._cache, key=lambda k: self._cache[k][0])
                del self._cache[oldest_key]
            self._cache[key] = (time.monotonic() + self._ttl, value)

    def invalidate(self, key: str | None = None) -> None:
        with self._lock:
            if key:
                self._cache.pop(key, None)
            else:
                self._cache.clear()

    @property
    def size(self) -> int:
        with self._lock:
            return len(self._cache)


def make_cache_key(*args: Any, **kwargs: Any) -> str:
    """Build a deterministic cache key from arguments."""
    raw = repr((args, sorted(kwargs.items())))
    return hashlib.md5(raw.encode()).hexdigest()


# Module-level caches for common query patterns
_embedding_cache = TTLCache(maxsize=128, ttl_seconds=600.0)
_search_cache = TTLCache(maxsize=256, ttl_seconds=120.0)


def cached_embed(embedder: Any, text: str) -> list[float]:
    """Cache embedding results to avoid redundant API calls."""
    key = make_cache_key("embed", text)
    result = _embedding_cache.get(key)
    if result is not None:
        return result
    result = embedder.embed_query(text)
    _embedding_cache.put(key, result)
    return result


def get_search_cache() -> TTLCache:
    """Return the shared search result cache."""
    return _search_cache


def get_embedding_cache() -> TTLCache:
    """Return the shared embedding cache."""
    return _embedding_cache
