import os
import sys
import redis
import json
import logging
from typing import Optional, Dict, Any, Callable
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Add genesis-memory to path for elestio_config
_genesis_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.path.insert(0, os.path.join(_genesis_root, "data", "genesis-memory"))

# Try to import Elestio config
try:
    import elestio_config
    ELESTIO_AVAILABLE = True
except ImportError:
    ELESTIO_AVAILABLE = False

logger = logging.getLogger("GenesisRedisConnector")


class RedisConnector:
    """
    Standardized Redis connector for Genesis system.
    Handles connection pooling, pub/sub, and stream operations.

    Now uses Elestio cloud configuration by default with proper ACL auth.
    Falls back to environment variables if Elestio config not available.
    """

    def __init__(self, use_elestio: bool = True):
        # Get configuration from Elestio or environment
        if use_elestio and ELESTIO_AVAILABLE:
            config = elestio_config.get_redis_config()
            self.host = config.host
            self.port = config.port
            self.username = config.user  # ACL auth requires username
            self.password = config.password
            self.db = 0
            logger.info(f"Using Elestio Redis: {self.host}:{self.port}")
        else:
            # Fallback to environment variables
            self.host = os.getenv("GENESIS_REDIS_HOST", os.getenv("REDIS_HOST", "localhost"))
            self.port = int(os.getenv("GENESIS_REDIS_PORT", os.getenv("REDIS_PORT", "6379")))
            self.username = os.getenv("GENESIS_REDIS_USER", os.getenv("REDIS_USER", "default"))
            self.password = os.getenv("GENESIS_REDIS_PASSWORD", os.getenv("REDIS_PASSWORD", None))
            self.db = int(os.getenv("REDIS_DB", 0))
            logger.info(f"Using env Redis: {self.host}:{self.port}")

        self.pool = redis.ConnectionPool(
            host=self.host,
            port=self.port,
            username=self.username,  # CRITICAL: ACL auth requires username
            password=self.password,
            db=self.db,
            decode_responses=True,
            socket_timeout=5.0,
            socket_connect_timeout=5.0,
            health_check_interval=30
        )
        self.client = redis.Redis(connection_pool=self.pool)
        self.pubsub = self.client.pubsub()

        # Verify connection
        try:
            self.client.ping()
            logger.info("Redis connection verified")
        except Exception as e:
            logger.error(f"Redis connection failed: {e}")

    def get(self, key: str) -> Optional[str]:
        return self.client.get(key)

    def set(self, key: str, value: Any, ex: Optional[int] = None) -> bool:
        if isinstance(value, (dict, list)):
            value = json.dumps(value)
        return self.client.set(key, value, ex=ex)

    def publish(self, channel: str, message: Dict[str, Any]) -> int:
        """Publish a JSON message to a channel."""
        return self.client.publish(channel, json.dumps(message))

    def subscribe(self, channel: str, handler: Callable[[Dict[str, Any]], None]):
        """Subscribe to a channel with a callback handler."""
        self.pubsub.subscribe(**{channel: handler})
        
        # Start a thread for processing messages if not already running
        # Note: In a production async environment, we might handle this differently
        if not self.pubsub.connection:
             self.pubsub.run_in_thread(sleep_time=0.001)

    def stream_add(self, stream_key: str, data: Dict[str, Any]) -> str:
        """Add an entry to a Redis stream."""
        return self.client.xadd(stream_key, data)

    def stream_read(self, stream_key: str, count: int = 1, block: int = 0):
        """Read from a Redis stream."""
        return self.client.xread({stream_key: '$'}, count=count, block=block)

    def close(self):
        self.client.close()

# Singleton instance
redis_client = RedisConnector()
