# AUTOGENERATED! DO NOT EDIT! File to edit: ../../nbs/2-Service.ipynb.

# %% auto 0
__all__ = ['Responder', 'ErrorHandler', 'DefaultErrorHandler', 'ChatService', 'echo_responder', 'ContextAwareResponder']

# %% ../../nbs/2-Service.ipynb 1
from typing import Protocol, Callable, Awaitable, Union, Optional, Any
import inspect
import asyncio
from .session import Message, ChatSession

# %% ../../nbs/2-Service.ipynb 3
class Responder(Protocol):
    """Protocol for chat responders (LLMs, APIs, etc.)."""

    async def __call__(self, message: str, context: Optional[Any] = None) -> str:
        """Process a message and return a response."""
        ...

# %% ../../nbs/2-Service.ipynb 4
class ErrorHandler(Protocol):
    """Protocol for handling errors during message processing."""

    def __call__(self, error: Exception, message: str) -> str:
        """Handle an error and return a user-friendly message."""
        ...

# %% ../../nbs/2-Service.ipynb 5
class DefaultErrorHandler:
    """Default error handler implementation."""

    def __init__(self, include_details: bool = True):
        self.include_details = include_details

    def __call__(self, error: Exception, message: str) -> str:
        """Handle an error and return a user-friendly message."""
        if self.include_details:
            return f"Error processing message: {type(error).__name__}: {str(error)}"
        return "Sorry, I encountered an error processing your message."

# %% ../../nbs/2-Service.ipynb 6
class ChatService:
    """Service for processing chat messages with responders."""

    def __init__(
        self,
        responder: Union[Responder, Callable],
        error_handler: Optional[ErrorHandler] = None,
        context_provider: Optional[Callable[[ChatSession], Any]] = None,
    ):
        """
        Initialize ChatService.

        Args:
            responder: Function/callable that processes messages and returns responses
            error_handler: Handler for processing errors
            context_provider: Optional function to extract context from session
        """
        self.responder = responder
        self.error_handler = error_handler or DefaultErrorHandler()
        self.context_provider = context_provider

    async def process_message(
        self, user_message: str, session: Optional[ChatSession] = None
    ) -> str:
        """
        Process a user message and return the assistant's response.

        Args:
            user_message: The user's input message
            session: Optional chat session for context

        Returns:
            The assistant's response string
        """
        try:
            # Extract context if provider exists
            context = None
            if self.context_provider and session:
                context = self.context_provider(session)

            # Call responder
            if inspect.iscoroutinefunction(self.responder):
                result = await self.responder(user_message, context)
            else:
                result = self.responder(user_message, context)
                if inspect.isawaitable(result):
                    result = await result

            return str(result)

        except Exception as e:
            return self.error_handler(e, user_message)

    async def process_session_message(
        self, session: ChatSession, user_message: str, add_to_session: bool = True
    ) -> Message:
        """
        Process a message within a session context.

        Args:
            session: The chat session
            user_message: The user's input
            add_to_session: Whether to add user message to session

        Returns:
            The assistant's response as a Message object
        """
        # Add user message if requested
        if add_to_session:
            session.add_message("User", user_message)

        # Process and get response
        response = await self.process_message(user_message, session)

        # Create and add assistant message
        assistant_msg = session.add_message("Assistant", response)
        return assistant_msg

# %% ../../nbs/2-Service.ipynb 8
async def echo_responder(message: str, context: Optional[Any] = None) -> str:
    """Simple echo responder for testing."""
    await asyncio.sleep(0.2)  # Simulate latency
    return f"[Echo] You said: {message}"

# %% ../../nbs/2-Service.ipynb 9
class ContextAwareResponder:
    """Example responder that uses conversation history."""

    def __init__(self, max_history: int = 5):
        self.max_history = max_history

    async def __call__(self, message: str, context: Optional[Any] = None) -> str:
        """Generate response with context awareness."""
        await asyncio.sleep(0.1)

        if context and isinstance(context, list):
            history_count = len(context)
            return f"I see we've exchanged {history_count} messages. You just said: {message}"

        return f"Hello! You said: {message}"
