"""Abstract base class and unified data types for LLM providers.

Internal message format is Anthropic-aligned to minimize conversion cost.
"""

from __future__ import annotations

from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Generator


# ---------------------------------------------------------------------------
# Content blocks (Anthropic-aligned)
# ---------------------------------------------------------------------------

@dataclass
class TextBlock:
    """A text content block."""
    text: str
    type: str = "text"


@dataclass
class ToolCallBlock:
    """A tool use request from the LLM."""
    id: str
    name: str
    input: dict[str, Any]
    type: str = "tool_use"


@dataclass
class ToolResultBlock:
    """Result of a tool execution, sent back to the LLM."""
    tool_use_id: str
    content: str
    is_error: bool = False
    type: str = "tool_result"


ContentBlock = TextBlock | ToolCallBlock | ToolResultBlock


# ---------------------------------------------------------------------------
# Messages
# ---------------------------------------------------------------------------

@dataclass
class Usage:
    """Token usage information."""
    input_tokens: int = 0
    output_tokens: int = 0


@dataclass
class AssistantMessage:
    """A complete assistant response (may contain multiple content blocks)."""
    content: list[ContentBlock] = field(default_factory=list)
    stop_reason: str | None = None
    usage: Usage = field(default_factory=Usage)


# ---------------------------------------------------------------------------
# Streaming events
# ---------------------------------------------------------------------------

class StreamEventType(Enum):
    TEXT_DELTA = "text_delta"
    TOOL_CALL_START = "tool_call_start"
    TOOL_CALL_DELTA = "tool_call_delta"
    TOOL_CALL_END = "tool_call_end"
    MESSAGE_END = "message_end"
    ERROR = "error"


@dataclass
class StreamEvent:
    """A single event from the streaming response."""
    type: StreamEventType
    # For TEXT_DELTA
    text: str = ""
    # For TOOL_CALL_*
    tool_call_id: str = ""
    tool_name: str = ""
    tool_input_delta: str = ""  # JSON string fragment
    # For MESSAGE_END
    stop_reason: str = ""
    # For ERROR
    error: str = ""
    # For MESSAGE_END (token usage)
    input_tokens: int = 0
    output_tokens: int = 0


# ---------------------------------------------------------------------------
# Tool definition (provider-agnostic)
# ---------------------------------------------------------------------------

@dataclass
class ToolDefinition:
    """Provider-agnostic tool definition."""
    name: str
    description: str
    parameters: dict[str, Any]  # JSON Schema


# ---------------------------------------------------------------------------
# Provider base
# ---------------------------------------------------------------------------

class ProviderBase(ABC):
    """Abstract base for LLM providers."""

    @abstractmethod
    def stream(
        self,
        messages: list[dict[str, Any]],
        system: str,
        tools: list[ToolDefinition] | None = None,
        max_tokens: int = 8192,
    ) -> Generator[StreamEvent, None, None]:
        """Stream a response from the LLM.

        Args:
            messages: Conversation history in Anthropic message format.
            system: System prompt.
            tools: Optional tool definitions.
            max_tokens: Maximum tokens in the response.

        Yields:
            StreamEvent objects as they arrive.
        """
        ...

    @abstractmethod
    def build_assistant_message(
        self, events: list[StreamEvent]
    ) -> AssistantMessage:
        """Reconstruct an AssistantMessage from collected stream events.

        This is used to append the assistant's response to the message history.
        """
        ...
