"""Redis cache layer — graceful fallback when Redis is unavailable."""

from __future__ import annotations

import json
import logging
import os
from typing import Any

import redis

logger = logging.getLogger(__name__)

_client: redis.Redis | None = None
_disabled = False

DEFAULT_TTL = 600  # 10 minutes


def _get_client() -> redis.Redis | None:
    global _client, _disabled
    if _disabled:
        return None
    if _client is None:
        host = os.environ.get("REDIS_HOST", "192.168.0.147")
        port = int(os.environ.get("REDIS_PORT", "6379"))
        db = int(os.environ.get("REDIS_DB", "0"))
        try:
            _client = redis.Redis(
                host=host, port=port, db=db,
                socket_connect_timeout=2,
                socket_timeout=2,
                decode_responses=True,
            )
            _client.ping()
            logger.info("Redis connected: %s:%s/%s", host, port, db)
        except Exception as e:
            logger.warning("Redis unavailable (%s), caching disabled", e)
            _client = None
            _disabled = True
            return None
    return _client


def make_key(*parts: Any) -> str:
    """Build a cache key like 'tasteby:restaurants:cuisine=한식:limit=100'."""
    return "tasteby:" + ":".join(str(p) for p in parts if p is not None and p != "")


def get(key: str) -> Any | None:
    """Get cached value. Returns None on miss or error."""
    try:
        client = _get_client()
        if not client:
            return None
        val = client.get(key)
        if val is not None:
            return json.loads(val)
    except Exception as e:
        logger.debug("Cache get error: %s", e)
    return None


def set(key: str, value: Any, ttl: int = DEFAULT_TTL) -> None:
    """Cache a value as JSON with TTL."""
    try:
        client = _get_client()
        if not client:
            return
        client.setex(key, ttl, json.dumps(value, ensure_ascii=False, default=str))
    except Exception as e:
        logger.debug("Cache set error: %s", e)


def flush() -> None:
    """Flush all tasteby cache keys."""
    try:
        client = _get_client()
        if not client:
            return
        cursor = 0
        while True:
            cursor, keys = client.scan(cursor, match="tasteby:*", count=200)
            if keys:
                client.delete(*keys)
            if cursor == 0:
                break
        logger.info("Cache flushed")
    except Exception as e:
        logger.debug("Cache flush error: %s", e)


def invalidate_prefix(prefix: str) -> None:
    """Delete all keys matching a prefix."""
    try:
        client = _get_client()
        if not client:
            return
        cursor = 0
        while True:
            cursor, keys = client.scan(cursor, match=f"{prefix}*", count=200)
            if keys:
                client.delete(*keys)
            if cursor == 0:
                break
    except Exception as e:
        logger.debug("Cache invalidate error: %s", e)
