import redis.asyncio as redis
import json
from typing import Any, Optional, Dict
from datetime import timedelta
import logging
from ...core.config import settings

logger = logging.getLogger(__name__)

class RedisCache:
    def __init__(self):
        self.redis_client: Optional[redis.Redis] = None
    
    async def connect(self):
        """Connect to Redis"""
        try:
            self.redis_client = redis.from_url(
                settings.REDIS_URL,
                encoding="utf-8",
                decode_responses=True
            )
            # Test connection
            await self.redis_client.ping()
            logger.info("Successfully connected to Redis")
        except Exception as e:
            logger.error(f"Error connecting to Redis: {e}")
            raise
    
    async def disconnect(self):
        """Disconnect from Redis"""
        if self.redis_client:
            await self.redis_client.close()
            logger.info("Redis connection closed")
    
    async def set(self, key: str, value: Any, ttl: Optional[int] = None):
        """Set a value in cache"""
        try:
            serialized_value = json.dumps(value, default=str)
            if ttl:
                await self.redis_client.setex(key, ttl, serialized_value)
            else:
                await self.redis_client.set(key, serialized_value)
            logger.debug(f"Cached value for key: {key}")
        except Exception as e:
            logger.error(f"Error setting cache for key {key}: {e}")
    
    async def get(self, key: str) -> Optional[Any]:
        """Get a value from cache"""
        try:
            value = await self.redis_client.get(key)
            if value:
                return json.loads(value)
            return None
        except Exception as e:
            logger.error(f"Error getting cache for key {key}: {e}")
            return None
    
    async def delete(self, key: str):
        """Delete a value from cache"""
        try:
            await self.redis_client.delete(key)
            logger.debug(f"Deleted cache for key: {key}")
        except Exception as e:
            logger.error(f"Error deleting cache for key {key}: {e}")
    
    async def exists(self, key: str) -> bool:
        """Check if key exists in cache"""
        try:
            return await self.redis_client.exists(key) > 0
        except Exception as e:
            logger.error(f"Error checking cache existence for key {key}: {e}")
            return False
    
    async def increment(self, key: str, amount: int = 1) -> int:
        """Increment a counter"""
        try:
            return await self.redis_client.incrby(key, amount)
        except Exception as e:
            logger.error(f"Error incrementing counter for key {key}: {e}")
            return 0
    
    async def set_hash(self, key: str, mapping: Dict[str, Any], ttl: Optional[int] = None):
        """Set hash values"""
        try:
            serialized_mapping = {k: json.dumps(v, default=str) for k, v in mapping.items()}
            await self.redis_client.hset(key, mapping=serialized_mapping)
            if ttl:
                await self.redis_client.expire(key, ttl)
        except Exception as e:
            logger.error(f"Error setting hash for key {key}: {e}")
    
    async def get_hash(self, key: str) -> Optional[Dict[str, Any]]:
        """Get hash values"""
        try:
            hash_data = await self.redis_client.hgetall(key)
            if hash_data:
                return {k: json.loads(v) for k, v in hash_data.items()}
            return None
        except Exception as e:
            logger.error(f"Error getting hash for key {key}: {e}")
            return None

redis_cache = RedisCache()
