"""
Boring-OS SDK flow data models.

Pydantic models for multi-agent flow operations,
including flow definitions, execution results, and streaming chunks.
"""

from __future__ import annotations

from datetime import datetime
from typing import Any

from pydantic import BaseModel, Field


# =============================================================================
# Flow Execution Result
# =============================================================================

class FlowNodeExecution(BaseModel):
    """Record of a single node execution within a flow."""

    node: str = Field(description="Node name in the flow graph")
    agent: str = Field(description="Agent that handled this node")
    duration_ms: int = Field(default=0, description="Execution time in milliseconds")


class FlowResult(BaseModel):
    """
    Result from a multi-agent flow execution.

    Returned by `boring.flows.run()`.
    """

    text: str = Field(default="", description="Final response text from the flow")
    nodes_executed: list[FlowNodeExecution] = Field(
        default_factory=list,
        description="Ordered list of nodes executed",
    )
    total_cost: float = Field(default=0.0, description="Cumulative cost in USD")
    execution_id: str = Field(default="", description="Unique execution identifier")
    total_duration_ms: int = Field(default=0, description="Total flow duration in ms")

    @property
    def node_count(self) -> int:
        """Number of nodes executed in the flow."""
        return len(self.nodes_executed)


class FlowStreamChunk(BaseModel):
    """
    A streaming chunk from a flow execution.

    Yielded by `boring.flows.stream()`.
    Includes node transition signals and per-node text output.
    """

    text: str = Field(default="", description="Incremental text content")
    done: bool = Field(default=False, description="True on the final chunk")
    node_changed: bool = Field(
        default=False,
        description="True when the flow transitions to a new node",
    )
    current_node: str | None = Field(
        default=None,
        description="Current node name (set on node transitions)",
    )
    nodes_executed: list[FlowNodeExecution] | None = Field(
        default=None,
        description="Full node execution list (only on final chunk)",
    )
    total_cost: float | None = Field(
        default=None,
        description="Total cost (only on final chunk)",
    )


# =============================================================================
# Flow Definition Models
# =============================================================================

class FlowEdge(BaseModel):
    """An edge in the flow graph connecting two nodes."""

    source: str = Field(description="Source node name")
    target: str = Field(description="Target node name")
    condition_type: str = Field(
        default="default",
        description="Condition type (keyword_match, regex, llm_classification, default)",
    )
    condition_value: str = Field(
        default="",
        description="Condition value (pattern or keyword)",
    )
    operation: str = Field(
        default="EVALUATE",
        description="Context passing operation (HANDOFF, EVALUATE, PARALLEL)",
    )


class FlowNode(BaseModel):
    """A node in the flow graph representing an agent step."""

    name: str = Field(description="Node name")
    agent: str = Field(description="Agent name to execute at this node")
    description: str = Field(default="", description="Node description")


class FlowDefinition(BaseModel):
    """
    Full flow definition with nodes, edges, and metadata.

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

    name: str = Field(description="Unique flow name")
    description: str = Field(default="", description="Human-readable description")
    nodes: list[FlowNode] = Field(
        default_factory=list,
        description="Nodes in the flow graph",
    )
    edges: list[FlowEdge] = Field(
        default_factory=list,
        description="Edges connecting nodes with conditions",
    )
    status: str = Field(default="active", description="Flow status")
    version: str = Field(default="1", description="Flow version")
    created_at: datetime | None = Field(default=None, description="Creation timestamp")
    updated_at: datetime | None = Field(default=None, description="Last update timestamp")


class FlowListResponse(BaseModel):
    """Response from listing flows."""

    flows: list[FlowDefinition] = Field(description="List of flow definitions")
    total: int = Field(description="Total number of flows matching filters")
