import json
import logging
from typing import Optional, Any, Dict
import redis
import os
from datetime import timedelta

logger = logging.getLogger(__name__)

class RedisClient:
    def __init__(self, redis_url: str = None):
        self.redis_url = redis_url or os.getenv('REDIS_URL', 'redis://localhost:6379')
        self.client = None
        self.connect()
    
    def connect(self):
        """Connect to Redis"""
        try:
            self.client = redis.from_url(self.redis_url, decode_responses=True)
            # Test connection
            self.client.ping()
            logger.info(f"Connected to Redis at {self.redis_url}")
        except Exception as e:
            logger.error(f"Failed to connect to Redis: {e}")
            raise
    
    def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool:
        """Set a key-value pair in Redis"""
        try:
            if isinstance(value, (dict, list)):
                value = json.dumps(value, default=str)
            
            result = self.client.set(key, value, ex=expire)
            logger.debug(f"Set key {key} in Redis")
            return result
        except Exception as e:
            logger.error(f"Failed to set key {key} in Redis: {e}")
            return False
    
    def get(self, key: str) -> Optional[Any]:
        """Get a value from Redis by key"""
        try:
            value = self.client.get(key)
            if value is None:
                return None
            
            # Try to parse as JSON
            try:
                return json.loads(value)
            except json.JSONDecodeError:
                return value
        except Exception as e:
            logger.error(f"Failed to get key {key} from Redis: {e}")
            return None
    
    def delete(self, key: str) -> bool:
        """Delete a key from Redis"""
        try:
            result = self.client.delete(key)
            logger.debug(f"Deleted key {key} from Redis")
            return bool(result)
        except Exception as e:
            logger.error(f"Failed to delete key {key} from Redis: {e}")
            return False
    
    def exists(self, key: str) -> bool:
        """Check if a key exists in Redis"""
        try:
            return bool(self.client.exists(key))
        except Exception as e:
            logger.error(f"Failed to check existence of key {key} in Redis: {e}")
            return False
    
    def expire(self, key: str, seconds: int) -> bool:
        """Set expiration time for a key"""
        try:
            result = self.client.expire(key, seconds)
            logger.debug(f"Set expiration for key {key} to {seconds} seconds")
            return result
        except Exception as e:
            logger.error(f"Failed to set expiration for key {key}: {e}")
            return False
    
    def hset(self, name: str, mapping: Dict[str, Any]) -> bool:
        """Set hash field values"""
        try:
            # Convert dict values to JSON strings if needed
            serialized_mapping = {}
            for k, v in mapping.items():
                if isinstance(v, (dict, list)):
                    serialized_mapping[k] = json.dumps(v, default=str)
                else:
                    serialized_mapping[k] = str(v)
            
            result = self.client.hset(name, mapping=serialized_mapping)
            logger.debug(f"Set hash {name} in Redis")
            return True
        except Exception as e:
            logger.error(f"Failed to set hash {name} in Redis: {e}")
            return False
    
    def hget(self, name: str, key: str) -> Optional[Any]:
        """Get hash field value"""
        try:
            value = self.client.hget(name, key)
            if value is None:
                return None
            
            # Try to parse as JSON
            try:
                return json.loads(value)
            except json.JSONDecodeError:
                return value
        except Exception as e:
            logger.error(f"Failed to get hash field {key} from {name}: {e}")
            return None
    
    def hgetall(self, name: str) -> Dict[str, Any]:
        """Get all hash field values"""
        try:
            data = self.client.hgetall(name)
            result = {}
            for k, v in data.items():
                try:
                    result[k] = json.loads(v)
                except json.JSONDecodeError:
                    result[k] = v
            return result
        except Exception as e:
            logger.error(f"Failed to get all hash fields from {name}: {e}")
            return {}
    
    def hdel(self, name: str, *keys: str) -> int:
        """Delete hash fields"""
        try:
            result = self.client.hdel(name, *keys)
            logger.debug(f"Deleted hash fields {keys} from {name}")
            return result
        except Exception as e:
            logger.error(f"Failed to delete hash fields from {name}: {e}")
            return 0
    
    def close(self):
        """Close Redis connection"""
        if self.client:
            self.client.close()
            logger.info("Redis connection closed")

# Cache key patterns
CACHE_KEYS = {
    'REQUEST_STATUS': 'request_status:{request_id}',
    'SEARCH_RESULTS': 'search_results:{request_id}',
    'METADATA_CACHE': 'metadata:{url_hash}',
    'USER_SESSION': 'session:{session_id}',
    'PROCESSING_QUEUE': 'processing_queue',
    'SERVICE_HEALTH': 'service_health:{service_name}'
}

def get_cache_key(pattern: str, **kwargs) -> str:
    """Generate a cache key from pattern and parameters"""
    return pattern.format(**kwargs)

# Default cache expiration times (in seconds)
CACHE_EXPIRATION = {
    'REQUEST_STATUS': 3600,  # 1 hour
    'SEARCH_RESULTS': 1800,  # 30 minutes
    'METADATA_CACHE': 86400,  # 24 hours
    'USER_SESSION': 1800,    # 30 minutes
    'SERVICE_HEALTH': 300    # 5 minutes
}
