import asyncio
from datetime import datetime
from typing import Dict, List, Optional, Any
from pydantic import BaseModel, Field


class Message(BaseModel):
    """A single message in a conversation."""
    role: str  # "user" or "assistant"
    content: str
    timestamp: datetime = Field(default_factory=datetime.utcnow)
    channel_id: Optional[str] = None
    metadata: Dict[str, Any] = Field(default_factory=dict)


class UserSession:
    """Manages a user's conversation session with the agent.

    A session tracks:
    - Active communication channels
    - Conversation history
    - In-flight agent tasks (for interruption)
    - User metadata
    """

    def __init__(self, session_id: str, user_id: str):
        self.session_id = session_id
        self.user_id = user_id
        self.active_channels: List[str] = []
        self.conversation_history: List[Message] = []
        self.current_task: Optional[asyncio.Task] = None
        self.metadata: Dict[str, Any] = {}
        self.created_at = datetime.utcnow()
        self.last_activity = datetime.utcnow()

    def add_channel(self, channel_id: str) -> None:
        """Attach a channel to this session."""
        if channel_id not in self.active_channels:
            self.active_channels.append(channel_id)
        self.last_activity = datetime.utcnow()

    def remove_channel(self, channel_id: str) -> None:
        """Detach a channel from this session."""
        if channel_id in self.active_channels:
            self.active_channels.remove(channel_id)
        self.last_activity = datetime.utcnow()

    def add_message(self, role: str, content: str, channel_id: Optional[str] = None, **kwargs) -> None:
        """Add a message to conversation history."""
        message = Message(
            role=role,
            content=content,
            channel_id=channel_id,
            metadata=kwargs
        )
        self.conversation_history.append(message)
        self.last_activity = datetime.utcnow()

    def get_history(self, limit: Optional[int] = None) -> List[Message]:
        """Get conversation history.

        Args:
            limit: Maximum number of recent messages to return (None = all)

        Returns:
            List of Message objects
        """
        if limit:
            return self.conversation_history[-limit:]
        return self.conversation_history

    async def interrupt(self) -> bool:
        """Interrupt the current agent task if one is running.

        Returns:
            True if a task was interrupted, False otherwise
        """
        if self.current_task and not self.current_task.done():
            self.current_task.cancel()
            try:
                await self.current_task
            except asyncio.CancelledError:
                pass
            self.current_task = None
            return True
        return False

    def set_task(self, task: asyncio.Task) -> None:
        """Set the current agent task."""
        self.current_task = task

    def is_busy(self) -> bool:
        """Check if the agent is currently processing a request."""
        return self.current_task is not None and not self.current_task.done()

    def to_dict(self) -> Dict[str, Any]:
        """Serialize session to dict."""
        return {
            "session_id": self.session_id,
            "user_id": self.user_id,
            "active_channels": self.active_channels,
            "message_count": len(self.conversation_history),
            "is_busy": self.is_busy(),
            "created_at": self.created_at.isoformat(),
            "last_activity": self.last_activity.isoformat(),
            "metadata": self.metadata
        }
