"""
Redis Storage Helper for storing shared state in cluster.
"""

import json
import os
from typing import Any, Dict, List, Optional

import redis
import structlog

logger = structlog.get_logger(__name__)


class RedisConnectionStorage:
    """
    Redis storage for active connections.

    Supports cluster operation with automatic TTL.
    """

    def __init__(self) -> None:
        """Initialize Redis connection storage."""
        self._redis_client = redis.Redis(
            host=os.getenv("REDIS_HOST", "redis"),
            port=int(os.getenv("REDIS_PORT", "6379")),
            password=os.getenv("REDIS_PASSWORD"),
            db=0,
            decode_responses=True,
        )

        try:
            self._redis_client.ping()
            logger.info("Redis Connection Storage initialized successfully")
        except Exception as e:
            logger.error("Failed to connect to Redis for connections", error=str(e))
            raise RuntimeError(f"Redis connection failed: {e}")
    
    def add_connection(
        self,
        connection_id: str,
        connection_data: Dict[str, Any],
        ttl_seconds: Optional[int] = None,
    ) -> None:
        """
        Add connection to Redis.

        Args:
            connection_id: Connection ID.
            connection_data: Connection data dictionary.
            ttl_seconds: TTL in seconds (None = no automatic expiration).
        """
        try:
            redis_key = f"connection:active:{connection_id}"
            if ttl_seconds is not None:
                self._redis_client.setex(
                    redis_key, ttl_seconds, json.dumps(connection_data)
                )
                logger.debug(
                    "Connection added to Redis with TTL",
                    connection_id=connection_id,
                    ttl_seconds=ttl_seconds,
                )
            else:
                self._redis_client.set(redis_key, json.dumps(connection_data))
                logger.debug(
                    "Connection added to Redis without TTL",
                    connection_id=connection_id,
                )
        except Exception as e:
            logger.error(
                "Failed to add connection to Redis",
                connection_id=connection_id,
                error=str(e),
            )
            raise
    
    def get_connection(self, connection_id: str) -> Optional[Dict[str, Any]]:
        """
        Get connection from Redis.

        Args:
            connection_id: Connection ID.

        Returns:
            Connection data dictionary or None if not found.
        """
        try:
            redis_key = f"connection:active:{connection_id}"
            conn_json = self._redis_client.get(redis_key)

            if not conn_json:
                return None

            return json.loads(conn_json)
        except Exception as e:
            logger.error(
                "Failed to get connection from Redis",
                connection_id=connection_id,
                error=str(e),
            )
            return None
    
    def update_connection(
        self, connection_id: str, update_data: Dict[str, Any]
    ) -> None:
        """
        Update connection data.

        Args:
            connection_id: Connection ID.
            update_data: Data to update (will be merged with existing data).
        """
        try:
            redis_key = f"connection:active:{connection_id}"

            conn_json = self._redis_client.get(redis_key)
            if not conn_json:
                logger.warning(
                    "Cannot update non-existent connection",
                    connection_id=connection_id,
                )
                return

            conn_data = json.loads(conn_json)
            conn_data.update(update_data)

            ttl = self._redis_client.ttl(redis_key)
            if ttl > 0:
                self._redis_client.setex(redis_key, ttl, json.dumps(conn_data))
                logger.debug("Connection updated in Redis", connection_id=connection_id)
        except Exception as e:
            logger.error(
                "Failed to update connection in Redis",
                connection_id=connection_id,
                error=str(e),
            )
    
    def delete_connection(self, connection_id: str) -> bool:
        """
        Delete connection from Redis.

        Args:
            connection_id: Connection ID.

        Returns:
            True if connection was deleted, False otherwise.
        """
        try:
            redis_key = f"connection:active:{connection_id}"
            result = self._redis_client.delete(redis_key)

            if result > 0:
                logger.debug("Connection deleted from Redis", connection_id=connection_id)
                return True
            return False
        except Exception as e:
            logger.error(
                "Failed to delete connection from Redis",
                connection_id=connection_id,
                error=str(e),
            )
            return False
    
    def get_all_connections(self) -> Dict[str, Dict[str, Any]]:
        """
        Get all active connections.

        Returns:
            Dictionary mapping connection_id to connection_data.
        """
        try:
            pattern = "connection:active:*"
            keys = list(self._redis_client.scan_iter(match=pattern, count=100))

            connections = {}
            for key in keys:
                try:
                    conn_id = key.replace("connection:active:", "")
                    conn_json = self._redis_client.get(key)
                    if conn_json:
                        connections[conn_id] = json.loads(conn_json)
                except Exception:
                    continue

            return connections
        except Exception as e:
            logger.error("Failed to get all connections from Redis", error=str(e))
            return {}
    
    def get_user_connections(self, username: str) -> List[Dict[str, Any]]:
        """
        Get all user connections.

        Args:
            username: Username.

        Returns:
            List of user connections.
        """
        try:
            all_connections = self.get_all_connections()
            user_connections = [
                conn_data
                for conn_data in all_connections.values()
                if conn_data.get("owner_username") == username
            ]

            return user_connections
        except Exception as e:
            logger.error(
                "Failed to get user connections from Redis",
                username=username,
                error=str(e),
            )
            return []
    
    def cleanup_expired_connections(self) -> int:
        """
        Cleanup expired connections.

        Returns:
            Number of removed connections.
        """
        try:
            pattern = "connection:active:*"
            keys = list(self._redis_client.scan_iter(match=pattern, count=100))

            cleaned_count = 0
            for key in keys:
                ttl = self._redis_client.ttl(key)
                if ttl == -2:
                    cleaned_count += 1
                elif ttl == -1:
                    self._redis_client.delete(key)
                    cleaned_count += 1

            if cleaned_count > 0:
                logger.info(
                    "Connections cleanup completed", cleaned_count=cleaned_count
                )

            return cleaned_count
        except Exception as e:
            logger.error("Failed to cleanup expired connections", error=str(e))
            return 0
    
    def get_stats(self) -> Dict[str, Any]:
        """
        Get connection statistics.

        Returns:
            Connection statistics dictionary.
        """
        try:
            pattern = "connection:active:*"
            keys = list(self._redis_client.scan_iter(match=pattern, count=100))

            return {"total_connections": len(keys), "storage": "Redis"}
        except Exception as e:
            logger.error("Failed to get connection stats", error=str(e))
            return {"error": str(e), "storage": "Redis"}


redis_connection_storage = RedisConnectionStorage()

