"""
Event types for the v2 LLM integration module.

Events are emitted during generation to provide visibility into the process.
"""

from dataclasses import dataclass
from dataclasses import field
from datetime import datetime
from enum import Enum
from typing import Any

# Type alias for tool result - can be any JSON-serializable value
ToolResultType = str | int | float | bool | None | list[Any] | dict[str, Any]


class EventType(Enum):
    """Types of events that can be emitted during generation."""

    # Streaming events
    TEXT_CHUNK = "text_chunk"  # Partial text during streaming
    REASONING_CHUNK = "reasoning_chunk"  # Partial reasoning/thinking during streaming

    # Completion events
    TEXT_DONE = "text_done"  # Final complete text

    # Tool events
    TOOL_CALL = "tool_call"  # Model wants to call a tool
    TOOL_RESULT = "tool_result"  # Tool execution completed

    # Compaction events
    COMPACTION_STARTED = "compaction_started"  # Context compaction started
    COMPACTION_DONE = "compaction_done"  # Context compaction completed

    # Meta events
    ERROR = "error"  # Error occurred
    RATE_LIMIT = "rate_limit"  # Rate limit hit, retrying
    DONE = "done"  # Generation complete


class FinishReason(Enum):
    """Reason why the model stopped generating."""

    STOP = "stop"  # Natural stop or stop sequence
    TOOL_CALLS = "tool_calls"  # Model called tools
    LENGTH = "length"  # Max tokens reached
    CONTENT_FILTER = "content_filter"  # Content policy violation (Azure)
    NULL = "null"  # Still generating (streaming)
    UNKNOWN = "unknown"  # Fallback/unknown reason


@dataclass
class TokenUsage:
    """Token counts for a generation or session."""

    prompt_tokens: int = 0
    completion_tokens: int = 0
    total_tokens: int = 0


@dataclass
class Usage:
    """
    Token usage statistics with current generation and session totals.

    Attributes:
        prompt_tokens: Input tokens for current generation
        completion_tokens: Output tokens for current generation
        total_tokens: Total tokens for current generation
        cached_tokens: Cached prompt tokens (OpenAI)
        audio_tokens: Audio input/output tokens
        reasoning_tokens: Tokens used for reasoning/thinking (chain-of-thought models)
        session: Cumulative token usage across the entire session/run
    """

    prompt_tokens: int = 0
    completion_tokens: int = 0
    total_tokens: int = 0
    cached_tokens: int = 0
    audio_tokens: int = 0
    reasoning_tokens: int = 0
    session: TokenUsage | None = None

    def has_usage(self) -> bool:
        """Check if this usage has any actual token counts."""
        return self.prompt_tokens > 0 or self.completion_tokens > 0 or self.total_tokens > 0


@dataclass
class Event:
    """Base event with common fields."""

    type: EventType
    timestamp: datetime = field(default_factory=datetime.now)
    usage: Usage = field(default_factory=Usage)
    extra: dict[str, Any] = field(default_factory=dict)


@dataclass
class TextChunkEvent(Event):
    """Streaming text chunk."""

    type: EventType = field(default=EventType.TEXT_CHUNK)
    chunk: str = ""


@dataclass
class ReasoningChunkEvent(Event):
    """Streaming reasoning/thinking chunk (e.g., from models with chain-of-thought)."""

    type: EventType = field(default=EventType.REASONING_CHUNK)
    chunk: str = ""


@dataclass
class TextDoneEvent(Event):
    """Complete text response."""

    type: EventType = field(default=EventType.TEXT_DONE)
    text: str = ""
    finish_reason: FinishReason = FinishReason.STOP


@dataclass
class ToolCallEvent(Event):
    """Model requesting a tool call."""

    type: EventType = field(default=EventType.TOOL_CALL)
    id: str = ""
    name: str = ""
    arguments: dict[str, Any] = field(default_factory=dict)
    finish_reason: FinishReason = FinishReason.TOOL_CALLS
    # Provider-specific metadata (e.g., Gemini thought signatures)
    metadata: dict[str, str] = field(default_factory=dict)


@dataclass
class ToolResultEvent(Event):
    """Result from tool execution."""

    type: EventType = field(default=EventType.TOOL_RESULT)
    id: str = ""
    name: str = ""
    result: ToolResultType = None
    error: str | None = None
    duration_ms: float = 0.0


@dataclass
class ErrorEvent(Event):
    """Error during generation."""

    type: EventType = field(default=EventType.ERROR)
    message: str = ""
    code: str | None = None
    recoverable: bool = False


@dataclass
class RateLimitEvent(Event):
    """Rate limit hit during generation, automatic retry in progress.

    Emitted each time a retryable HTTP error (429 or 5xx) is encountered
    and the provider will retry after a delay.
    """

    type: EventType = field(default=EventType.RATE_LIMIT)
    attempt: int = 0  # Current retry attempt (1-based)
    max_retries: int = 0  # Maximum retry attempts configured
    retry_after: float = 0.0  # Delay in seconds before retry
    status_code: int = 0  # HTTP status code (429, 500, etc.)
    message: str = ""  # Human-readable message
    rate_limit_info: dict[str, str] = field(default_factory=dict)  # x-ratelimit-* headers


@dataclass
class DoneEvent(Event):
    """Generation complete."""

    type: EventType = field(default=EventType.DONE)
    final_text: str = ""
    session_id: str | None = None
    finish_reason: FinishReason = FinishReason.STOP


@dataclass
class CompactionStartedEvent(Event):
    """Event emitted when compaction begins.

    Attributes:
        trigger: The trigger that caused compaction (Tokens or Messages)
        message_count: Number of messages before compaction
        estimated_tokens: Estimated token count before compaction
        session_id: Original session ID
        compaction_session_id: Session ID used for compaction
    """

    type: EventType = field(default=EventType.COMPACTION_STARTED)
    trigger: Any = None  # Tokens | Messages - using Any to avoid circular import
    message_count: int = 0
    estimated_tokens: int = 0
    session_id: str = ""
    compaction_session_id: str = ""


@dataclass
class CompactionDoneEvent(Event):
    """Event emitted when compaction completes.

    Attributes:
        original_message_count: Number of messages before compaction
        original_token_count: Estimated tokens before compaction
        new_message_count: Number of messages after compaction
        summary_tokens: Estimated tokens in the summary
        summary_text: Full summary text
        trigger: The trigger that caused compaction
        compactor_used: Model name used for compaction ("self" or model name)
        session_id: Original session ID
        compaction_session_id: Session ID used for compaction
    """

    type: EventType = field(default=EventType.COMPACTION_DONE)
    original_message_count: int = 0
    original_token_count: int = 0
    new_message_count: int = 0
    summary_tokens: int = 0
    summary_text: str = ""
    trigger: Any = None  # Tokens | Messages - using Any to avoid circular import
    compactor_used: str = ""
    session_id: str = ""
    compaction_session_id: str = ""
