"""SSE streaming infrastructure for agent events."""

import asyncio
import json
from datetime import datetime
from enum import Enum
from typing import Any, AsyncIterator

from pydantic import BaseModel


class EventType(str, Enum):
    """Types of events emitted by agents (matches emdash.agent.events.EventType)."""

    # Tool lifecycle
    TOOL_START = "tool_start"
    TOOL_RESULT = "tool_result"

    # Sub-agent lifecycle
    SUBAGENT_START = "subagent_start"
    SUBAGENT_END = "subagent_end"

    # Agent thinking/progress
    THINKING = "thinking"
    PROGRESS = "progress"

    # Output
    RESPONSE = "response"
    PARTIAL_RESPONSE = "partial_response"

    # Interaction
    CLARIFICATION = "clarification"
    CLARIFICATION_RESPONSE = "clarification_response"
    PLAN_SUBMITTED = "plan_submitted"

    # Errors
    ERROR = "error"
    WARNING = "warning"

    # Session
    SESSION_START = "session_start"
    SESSION_END = "session_end"

    # Context
    CONTEXT_FRAME = "context_frame"


class SSEEvent(BaseModel):
    """An SSE event for streaming to clients."""

    type: EventType
    data: dict[str, Any]
    timestamp: datetime
    agent_name: str | None = None

    def to_sse(self) -> str:
        """Format as SSE wire protocol.

        Returns:
            SSE formatted string: "event: {type}\ndata: {json}\n\n"
        """
        payload = {
            **self.data,
            "timestamp": self.timestamp.isoformat(),
        }
        if self.agent_name:
            payload["agent_name"] = self.agent_name

        data_json = json.dumps(payload, default=str)
        return f"event: {self.type.value}\ndata: {data_json}\n\n"


class SSEHandler:
    """Event handler that queues events for SSE streaming.

    This handler implements the EventHandler protocol and collects
    events into an async queue for streaming to HTTP clients.

    Thread-safe: can be used from both async context and thread pools.
    """

    def __init__(self, agent_name: str | None = None):
        """Initialize the handler.

        Args:
            agent_name: Optional name to tag events
        """
        # Use thread-safe queue for cross-thread communication
        import queue
        self._queue: queue.Queue[SSEEvent | None] = queue.Queue()
        self._agent_name = agent_name
        self._closed = False

    def handle(self, event: Any) -> None:
        """Handle an event from the agent.

        Args:
            event: AgentEvent from the agent system
        """
        if self._closed:
            return

        # Convert to SSEEvent
        sse_event = SSEEvent(
            type=EventType(event.type.value),
            data=event.data,
            timestamp=event.timestamp,
            agent_name=event.agent_name or self._agent_name,
        )

        # Put in queue (thread-safe)
        self._queue.put(sse_event)

    def emit(self, event_type: EventType, data: dict[str, Any]) -> None:
        """Emit an event directly (thread-safe).

        Args:
            event_type: Type of event
            data: Event data
        """
        if self._closed:
            return

        sse_event = SSEEvent(
            type=event_type,
            data=data,
            timestamp=datetime.now(),
            agent_name=self._agent_name,
        )

        self._queue.put(sse_event)

    async def emit_async(self, event_type: EventType, data: dict[str, Any]) -> None:
        """Emit an event asynchronously.

        Args:
            event_type: Type of event
            data: Event data
        """
        if self._closed:
            return

        sse_event = SSEEvent(
            type=event_type,
            data=data,
            timestamp=datetime.now(),
            agent_name=self._agent_name,
        )

        self._queue.put(sse_event)

    def close(self) -> None:
        """Close the handler and signal end of stream."""
        self._closed = True
        self._queue.put(None)  # Sentinel to end iteration

    async def __aiter__(self) -> AsyncIterator[str]:
        """Iterate over SSE-formatted events.

        Yields:
            SSE formatted strings ready for HTTP streaming
        """
        import queue

        loop = asyncio.get_event_loop()
        ping_counter = 0

        while True:
            try:
                # Run blocking queue.get in executor to not block event loop
                try:
                    event = await loop.run_in_executor(
                        None,  # Default executor
                        lambda: self._queue.get(timeout=1.0)
                    )
                    if event is None:
                        break
                    yield event.to_sse()
                    ping_counter = 0
                except queue.Empty:
                    ping_counter += 1
                    # Send keep-alive ping every 30 seconds
                    if ping_counter >= 30:
                        yield ": ping\n\n"
                        ping_counter = 0
                    continue
            except Exception:
                break


async def create_sse_stream(handler: SSEHandler) -> AsyncIterator[str]:
    """Create an SSE stream from a handler.

    Args:
        handler: SSE handler to stream from

    Yields:
        SSE formatted event strings
    """
    async for event in handler:
        yield event
