"""
Redis 캐싱 유틸리티
"""
import redis
import json
import logging
from typing import Optional, Any
from functools import wraps

logger = logging.getLogger(__name__)


class RedisCache:
    """Redis 캐싱 클래스"""

    def __init__(
        self,
        host: str = "localhost",
        port: int = 6379,
        db: int = 0,
        password: Optional[str] = None,
        decode_responses: bool = True
    ):
        """
        초기화

        Args:
            host: Redis 호스트
            port: Redis 포트
            db: Redis DB 번호
            password: Redis 비밀번호
            decode_responses: 응답 디코딩 여부
        """
        try:
            self.client = redis.Redis(
                host=host,
                port=port,
                db=db,
                password=password,
                decode_responses=decode_responses
            )
            # 연결 테스트
            self.client.ping()
            logger.info(f"Redis 연결 성공: {host}:{port}")
        except Exception as e:
            logger.warning(f"Redis 연결 실패: {str(e)} - 캐싱 비활성화")
            self.client = None

    def get(self, key: str) -> Optional[Any]:
        """
        캐시에서 값 조회

        Args:
            key: 캐시 키

        Returns:
            캐시된 값 또는 None
        """
        if not self.client:
            return None

        try:
            value = self.client.get(key)
            if value:
                logger.debug(f"캐시 HIT: {key}")
                return json.loads(value)
            logger.debug(f"캐시 MISS: {key}")
            return None
        except Exception as e:
            logger.error(f"캐시 조회 실패 ({key}): {str(e)}")
            return None

    def set(self, key: str, value: Any, ttl: int = 3600) -> bool:
        """
        캐시에 값 저장

        Args:
            key: 캐시 키
            value: 저장할 값
            ttl: 만료 시간 (초)

        Returns:
            성공 여부
        """
        if not self.client:
            return False

        try:
            serialized = json.dumps(value, ensure_ascii=False)
            self.client.setex(key, ttl, serialized)
            logger.debug(f"캐시 저장: {key} (TTL: {ttl}s)")
            return True
        except Exception as e:
            logger.error(f"캐시 저장 실패 ({key}): {str(e)}")
            return False

    def delete(self, key: str) -> bool:
        """
        캐시 삭제

        Args:
            key: 캐시 키

        Returns:
            성공 여부
        """
        if not self.client:
            return False

        try:
            self.client.delete(key)
            logger.debug(f"캐시 삭제: {key}")
            return True
        except Exception as e:
            logger.error(f"캐시 삭제 실패 ({key}): {str(e)}")
            return False

    def delete_pattern(self, pattern: str) -> int:
        """
        패턴 매칭으로 여러 캐시 삭제

        Args:
            pattern: 캐시 키 패턴 (예: "minutes:*")

        Returns:
            삭제된 키 개수
        """
        if not self.client:
            return 0

        try:
            keys = self.client.keys(pattern)
            if keys:
                count = self.client.delete(*keys)
                logger.info(f"캐시 일괄 삭제: {count}개 키 ({pattern})")
                return count
            return 0
        except Exception as e:
            logger.error(f"캐시 패턴 삭제 실패 ({pattern}): {str(e)}")
            return 0

    def exists(self, key: str) -> bool:
        """
        캐시 존재 여부 확인

        Args:
            key: 캐시 키

        Returns:
            존재 여부
        """
        if not self.client:
            return False

        try:
            return self.client.exists(key) > 0
        except Exception as e:
            logger.error(f"캐시 존재 확인 실패 ({key}): {str(e)}")
            return False


def cached(prefix: str, ttl: int = 3600, key_builder=None):
    """
    함수 결과 캐싱 데코레이터

    Args:
        prefix: 캐시 키 prefix
        ttl: 만료 시간 (초)
        key_builder: 캐시 키 생성 함수 (선택사항)

    Example:
        @cached(prefix="minutes:", ttl=1800)
        def get_minutes_by_id(minutes_id: str):
            ...
    """
    def decorator(func):
        @wraps(func)
        def wrapper(self, *args, **kwargs):
            # Redis 캐시 인스턴스 확인
            cache = getattr(self, '_cache', None)
            if not cache or not cache.client:
                return func(self, *args, **kwargs)

            # 캐시 키 생성
            if key_builder:
                cache_key = key_builder(*args, **kwargs)
            else:
                # 기본: 첫 번째 인자를 키로 사용
                cache_key = f"{prefix}{args[0] if args else ''}"

            # 캐시 조회
            cached_value = cache.get(cache_key)
            if cached_value is not None:
                return cached_value

            # 함수 실행
            result = func(self, *args, **kwargs)

            # 결과 캐싱
            if result is not None:
                cache.set(cache_key, result, ttl)

            return result

        return wrapper
    return decorator
