"""
Context Management for Breakpilot Agents

Manages conversation contexts for multiple sessions with persistence.
"""

from typing import Dict, Any, List, Optional, Callable, Awaitable
import json
import logging

from agent_core.brain.context_models import (
    MessageRole,
    Message,
    ConversationContext,
)

logger = logging.getLogger(__name__)


class ContextManager:
    """
    Manages conversation contexts for multiple sessions.

    Provides:
    - Context creation and retrieval
    - Persistence to Valkey/PostgreSQL
    - Context sharing between agents
    """

    def __init__(
        self,
        redis_client=None,
        db_pool=None,
        namespace: str = "breakpilot"
    ):
        """
        Initialize the context manager.

        Args:
            redis_client: Async Redis/Valkey client
            db_pool: Async PostgreSQL connection pool
            namespace: Key namespace
        """
        self.redis = redis_client
        self.db_pool = db_pool
        self.namespace = namespace
        self._contexts: Dict[str, ConversationContext] = {}
        self._summarize_callback: Optional[Callable[[List[Message]], Awaitable[str]]] = None

    def _redis_key(self, session_id: str) -> str:
        """Generate Redis key for context"""
        return f"{self.namespace}:context:{session_id}"

    def create_context(
        self,
        session_id: str,
        system_prompt: Optional[str] = None,
        max_messages: int = 50
    ) -> ConversationContext:
        """
        Creates a new conversation context.

        Args:
            session_id: Session ID for this context
            system_prompt: Optional system prompt
            max_messages: Maximum messages before compression

        Returns:
            The created context
        """
        context = ConversationContext(
            max_messages=max_messages,
            system_prompt=system_prompt
        )
        self._contexts[session_id] = context
        return context

    async def get_context(self, session_id: str) -> Optional[ConversationContext]:
        """
        Gets a context by session ID.

        Args:
            session_id: The session ID

        Returns:
            ConversationContext or None
        """
        # Check local cache
        if session_id in self._contexts:
            return self._contexts[session_id]

        # Try Valkey
        context = await self._get_from_valkey(session_id)
        if context:
            self._contexts[session_id] = context
            return context

        return None

    async def save_context(self, session_id: str) -> None:
        """
        Saves a context to persistent storage.

        Args:
            session_id: The session ID
        """
        if session_id not in self._contexts:
            return

        context = self._contexts[session_id]
        await self._cache_in_valkey(session_id, context)

    async def delete_context(self, session_id: str) -> bool:
        """
        Deletes a context.

        Args:
            session_id: The session ID

        Returns:
            True if deleted
        """
        self._contexts.pop(session_id, None)

        if self.redis:
            await self.redis.delete(self._redis_key(session_id))

        return True

    def set_summarize_callback(
        self,
        callback: Callable[[List[Message]], Awaitable[str]]
    ) -> None:
        """
        Sets a callback for LLM-based summarization.

        Args:
            callback: Async function that takes messages and returns summary
        """
        self._summarize_callback = callback

    async def add_message(
        self,
        session_id: str,
        role: MessageRole,
        content: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> Optional[Message]:
        """
        Adds a message to a session's context.

        Args:
            session_id: The session ID
            role: Message role
            content: Message content
            metadata: Optional metadata

        Returns:
            The created message or None if context not found
        """
        context = await self.get_context(session_id)
        if not context:
            return None

        message = context.add_message(role, content, metadata)

        # Save after each message
        await self.save_context(session_id)

        return message

    async def get_messages_for_llm(
        self,
        session_id: str
    ) -> Optional[List[Dict[str, str]]]:
        """
        Gets formatted messages for LLM API call.

        Args:
            session_id: The session ID

        Returns:
            List of message dicts or None
        """
        context = await self.get_context(session_id)
        if not context:
            return None

        return context.get_messages_for_llm()

    async def _cache_in_valkey(
        self,
        session_id: str,
        context: ConversationContext
    ) -> None:
        """Caches context in Valkey"""
        if not self.redis:
            return

        try:
            # 24 hour TTL for contexts
            await self.redis.setex(
                self._redis_key(session_id),
                86400,
                json.dumps(context.to_dict())
            )
        except Exception as e:
            logger.warning(f"Failed to cache context in Valkey: {e}")

    async def _get_from_valkey(
        self,
        session_id: str
    ) -> Optional[ConversationContext]:
        """Retrieves context from Valkey"""
        if not self.redis:
            return None

        try:
            data = await self.redis.get(self._redis_key(session_id))
            if data:
                return ConversationContext.from_dict(json.loads(data))
        except Exception as e:
            logger.warning(f"Failed to get context from Valkey: {e}")

        return None
