import redis
import time
import logging
import os
from typing import Dict, Any

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class RedisMetricsCollector:
    """
    Collects and exposes Redis performance metrics.
    """

    def __init__(self, host: str, port: int):
        """
        Initializes the RedisMetricsCollector.

        Args:
            host (str): The Redis host.
            port (int): The Redis port.
        """
        self.host = host
        self.port = port
        self.redis_client = None
        self.connect()

    def connect(self):
        """
        Connects to the Redis server.
        """
        try:
            self.redis_client = redis.Redis(host=self.host, port=self.port, decode_responses=True)
            self.redis_client.ping()
            logging.info(f"Successfully connected to Redis at {self.host}:{self.port}")
        except redis.exceptions.ConnectionError as e:
            logging.error(f"Failed to connect to Redis at {self.host}:{self.port}: {e}")
            self.redis_client = None

    def get_metrics(self) -> Dict[str, Any]:
        """
        Retrieves Redis performance metrics.

        Returns:
            Dict[str, Any]: A dictionary containing the Redis metrics.  Returns an empty dict if Redis is not connected.
        """
        if not self.redis_client:
            logging.warning("Redis client is not connected.  Returning empty metrics.")
            return {}

        try:
            info = self.redis_client.info()
            metrics = {
                'memory_used': info.get('used_memory'),
                'commands_per_sec': info.get('instantaneous_ops_per_sec'),
                'connected_clients': info.get('connected_clients'),
                'keyspace_hits': info.get('keyspace_hits'),
            }
            return metrics
        except redis.exceptions.ConnectionError as e:
            logging.error(f"Failed to retrieve metrics from Redis: {e}")
            self.connect()  # Attempt to reconnect
            return {}
        except Exception as e:
            logging.error(f"An unexpected error occurred while retrieving metrics: {e}")
            return {}

    def check_memory_usage(self) -> bool:
        """
        Checks if Redis memory usage exceeds 80%.

        Returns:
            bool: True if memory usage is above 80%, False otherwise.  Returns False if Redis is not connected.
        """
        if not self.redis_client:
            logging.warning("Redis client is not connected.  Skipping memory check.")
            return False

        try:
            info = self.redis_client.info()
            used_memory = info.get('used_memory')
            max_memory = info.get('maxmemory')

            if max_memory == 0:  # maxmemory is not set, so no limit
                logging.warning("Redis maxmemory is not set, skipping memory usage check.")
                return False
            
            if used_memory is None:
                logging.error("used_memory is None. Unable to check memory usage.")
                return False

            if max_memory is None:
                logging.error("max_memory is None. Unable to check memory usage.")
                return False


            memory_usage_percentage = (used_memory / max_memory) * 100
            logging.info(f"Redis memory usage: {memory_usage_percentage:.2f}%")
            return memory_usage_percentage > 80
        except redis.exceptions.ConnectionError as e:
            logging.error(f"Failed to check memory usage: {e}")
            self.connect()  # Attempt to reconnect
            return False
        except Exception as e:
            logging.error(f"An unexpected error occurred while checking memory usage: {e}")
            return False

if __name__ == '__main__':
    # Example Usage
    redis_host = os.environ.get("REDIS_HOST", "redis-genesis-u50607.vm.elestio.app")
    redis_port = int(os.environ.get("REDIS_PORT", 26379))

    collector = RedisMetricsCollector(redis_host, redis_port)

    while True:
        metrics = collector.get_metrics()
        if metrics:
            logging.info(f"Redis Metrics: {metrics}")

        if collector.check_memory_usage():
            logging.warning("Redis memory usage is above 80%!")
        else:
            logging.info("Redis memory usage is within acceptable limits.")

        time.sleep(60)  # Collect metrics every 60 seconds
