"""Node output models — Pydantic SSOT for every node's return shape.

ALL PYDANTIC. No dict[str, Any]. No Any.
Every field is typed with JSONValue, JsonObject, ToolArgValue, or a concrete model.
"""

from typing import Literal

from pydantic import BaseModel, ConfigDict, Field

from core.common import JsonObject, JSONValue
from core.pathways.models.io import NodeOutputs

# =============================================================================
# PER-NODE OUTPUT MODELS (extend NodeOutputs)
# =============================================================================


class ToolExecutionResultItem(BaseModel):
    """Result of a single tool call inside LLMNode."""

    tool: str
    success: bool
    output: JSONValue = None
    error: str | None = None


class CodeExecutionOutput(NodeOutputs):
    """Output from CodeExecuteNode."""

    success: bool
    result: JSONValue = None
    stdout: str = ""
    stderr: str = ""
    error: str | None = None
    duration_ms: float = 0.0


class PathwayExecutionOutput(NodeOutputs):
    """Output from PathwayNode (sub-pathway execution)."""

    success: bool
    pathway_id: str
    result: JSONValue = None
    outputs: JsonObject = Field(default_factory=dict)
    error: str | None = None


class ConditionalOutput(NodeOutputs):
    """Output from ConditionalNode.

    ``selected_route`` mirrors ``next_node`` so that ``should_execute()``
    in the DAG scheduler can gate downstream branches.  Without it, both
    the true and false branches execute unconditionally.
    """

    branch: Literal["true", "false"]
    next_node: str
    selected_route: str  # == next_node; checked by should_execute()
    condition_result: bool


class StageOutput(NodeOutputs):
    """Output from StageNode."""

    stage_id: str
    goal: str
    agent_result: JsonObject = Field(default_factory=dict)
    next_stage: str | None = None
    transition_reason: str | None = None
    success: bool = True


class LoopOutput(NodeOutputs):
    """Output from LoopNode."""

    should_continue: bool = Field(alias="continue")
    iteration: int
    body_nodes: list[str] = Field(default_factory=list)
    reason: str | None = None

    model_config = ConfigDict(populate_by_name=True)


class CheckpointOutput(NodeOutputs):
    """Output from CheckpointNode."""

    checkpoint_id: str
    saved: bool
    require_approval: bool
    assignee_role: str | None = None
    keys_saved: list[str] = Field(default_factory=list)


class InputSignature(BaseModel):
    """Signature returned by InputNode.get_signature()."""

    inputs: JsonObject = Field(default_factory=dict)
    defaults: JsonObject = Field(default_factory=dict)
    description: str = ""


class OutputSignature(BaseModel):
    """Signature returned by OutputNode.get_signature()."""

    outputs: JsonObject = Field(default_factory=dict)
    mapping: JsonObject = Field(default_factory=dict)
    description: str = ""


__all__ = [
    "CheckpointOutput",
    "CodeExecutionOutput",
    "ConditionalOutput",
    "InputSignature",
    "LoopOutput",
    "OutputSignature",
    "PathwayExecutionOutput",
    "StageOutput",
    "ToolExecutionResultItem",
]
