"""Redis hot cache for search results (master plan sec 7.2)."""

from __future__ import annotations

import hashlib
import json
import logging
from functools import lru_cache
from typing import Any

import redis.asyncio as redis_async

from app.core.config import settings

logger = logging.getLogger(__name__)


class SearchCache:
    """Async Redis cache wrapper. Stable sha256 hash keys, TTL per kind."""

    TTL_SEARCH: int = 300
    TTL_HOUSE_DETAIL: int = 3600
    TTL_VALUATION: int = 86400

    def __init__(self, url: str) -> None:
        self._client: redis_async.Redis = redis_async.from_url(
            url, decode_responses=True, socket_timeout=2.0, socket_connect_timeout=2.0
        )

    def search_key(self, params: dict[str, Any]) -> str:
        payload = json.dumps(params, sort_keys=True, default=str, ensure_ascii=False)
        digest = hashlib.sha256(payload.encode("utf-8")).hexdigest()[:32]
        return f"tradein:search:{digest}"

    async def get(self, key: str) -> dict[str, Any] | None:
        try:
            raw = await self._client.get(key)
        except Exception as e:
            logger.warning("redis GET failed key=%s: %s", key[:24], e)
            return None
        if raw is None:
            return None
        try:
            return json.loads(raw)
        except json.JSONDecodeError:
            logger.warning("redis cache corrupted key=%s", key[:24])
            return None

    async def set(self, key: str, value: dict[str, Any], ttl: int) -> None:
        try:
            await self._client.set(
                key, json.dumps(value, default=str, ensure_ascii=False), ex=ttl
            )
        except Exception as e:
            logger.warning("redis SET failed key=%s: %s", key[:24], e)

    async def close(self) -> None:
        await self._client.aclose()


@lru_cache(maxsize=1)
def get_search_cache() -> SearchCache:
    """Singleton — один Redis pool на процесс."""
    return SearchCache(settings.redis_url)
