"""
Boring-OS SDK agent data models.

Pydantic models for agent-related requests and responses,
including agent definitions, execution results, streaming chunks,
and session info.
"""

from __future__ import annotations

from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field


# =============================================================================
# Token Usage
# =============================================================================

class TokenUsage(BaseModel):
    """Token usage breakdown for an agent response."""

    input: int = Field(default=0, description="Input tokens consumed")
    output: int = Field(default=0, description="Output tokens generated")
    cost: float = Field(default=0.0, description="Estimated cost in USD")


# =============================================================================
# Agent Response Models
# =============================================================================

class AgentResponse(BaseModel):
    """
    Response from a single-turn or session-based agent call.

    Returned by `boring.agents.run()` and `session.send()`.
    """

    text: str = Field(description="Final assistant response text")
    tool_calls: list[dict[str, Any]] = Field(
        default_factory=list,
        description="Tools invoked during the agent loop",
    )
    token_usage: TokenUsage = Field(
        default_factory=TokenUsage,
        description="Token usage breakdown",
    )
    iterations: int = Field(default=1, description="Number of model loop iterations")
    model_used: str = Field(default="", description="Model used for the response")
    session_id: str | None = Field(default=None, description="Session ID (if session-based)")
    request_id: str = Field(default="", description="Unique request identifier")

    @property
    def cost(self) -> float:
        """Shorthand for token_usage.cost."""
        return self.token_usage.cost


class AgentStreamChunk(BaseModel):
    """
    A streaming chunk from an agent call.

    Yielded by `boring.agents.stream()` and `session.stream()`.
    The final chunk has `done=True` and includes token_usage.
    """

    text: str = Field(default="", description="Incremental text content")
    done: bool = Field(default=False, description="True on the final chunk")
    tool_calls: list[dict[str, Any]] = Field(
        default_factory=list,
        description="Tool calls completed (populated mid-stream)",
    )
    token_usage: TokenUsage | None = Field(
        default=None,
        description="Token usage (only on final chunk)",
    )
    node_changed: bool = Field(
        default=False,
        description="True when flow transitions to a new node",
    )
    current_node: str | None = Field(
        default=None,
        description="Current flow node name (if in a flow)",
    )


# =============================================================================
# Agent Definition Models
# =============================================================================

class AgentModelConfig(BaseModel):
    """Model-specific configuration for an agent."""

    temperature: float = Field(default=0.7, description="Sampling temperature")
    max_tokens: int = Field(default=4096, description="Max output tokens per call")
    top_p: float = Field(default=1.0, description="Top-p sampling")


class AgentDefinition(BaseModel):
    """
    Full agent definition with configuration and metadata.

    Returned by `boring.agents.get()` and `boring.agents.register()`.
    """

    name: str = Field(description="Unique agent name")
    description: str = Field(default="", description="Human-readable description")
    model: str = Field(default="", description="LLM model name")
    model_config_: AgentModelConfig | None = Field(
        default=None,
        alias="model_config",
        description="Model-specific parameters",
    )
    tools: list[str] = Field(
        default_factory=list,
        description="List of tool names the agent can use",
    )
    prompt_id: str | None = Field(
        default=None,
        description="Prompt registry ID for the system prompt",
    )
    knowledge_sources: list[str] = Field(
        default_factory=list,
        description="Knowledge source IDs for RAG retrieval",
    )
    max_iterations: int = Field(default=25, description="Max model loop iterations")
    max_total_tokens: int = Field(
        default=100_000,
        description="Max cumulative tokens across iterations",
    )
    status: str = Field(default="active", description="Agent status (active, deprecated)")
    version: str = Field(default="1", description="Agent version")
    created_at: datetime | None = Field(default=None, description="Creation timestamp")
    updated_at: datetime | None = Field(default=None, description="Last update timestamp")

    # Allow population by field name or alias
    model_config = {"populate_by_name": True}  # type: ignore[assignment]


class AgentSummary(BaseModel):
    """Summary agent info for listing responses."""

    name: str = Field(description="Agent name")
    description: str = Field(default="", description="Brief description")
    model: str = Field(default="", description="Model used")
    status: str = Field(default="active", description="Agent status")
    version: str = Field(default="1", description="Current version")
    tools_count: int = Field(default=0, description="Number of tools configured")


class AgentListResponse(BaseModel):
    """Response from listing agents."""

    agents: list[AgentSummary] = Field(description="List of agent summaries")
    total: int = Field(description="Total number of agents matching filters")


# =============================================================================
# Session Models
# =============================================================================

class AgentSessionInfo(BaseModel):
    """
    Information about an active agent session.

    Returned by `boring.agents.create_session()`.
    """

    session_id: str = Field(description="Unique session identifier")
    agent_name: str = Field(description="Agent associated with this session")
    total_messages: int = Field(default=0, description="Messages exchanged so far")
    total_cost: float = Field(default=0.0, description="Cumulative cost in USD")
    created_at: datetime | None = Field(default=None, description="Session creation time")
