"""Tool actor context for CleverAgents execution actors.

The ``ToolActorContext`` carries all the state needed for a tool-calling
actor execution loop: project resources, sandbox configuration, plan
metadata, resource bindings, and a running history of tool calls made
during the current run.

Based on ``docs/specification.md`` and ``implementation_plan.md`` task
M1.actor-runtime.
"""

from __future__ import annotations

from typing import Any

from pydantic import BaseModel, ConfigDict, Field


class ToolCallRecord(BaseModel):
    """Structured record of a single tool call during an actor run.

    Captures the tool name, inputs sent, outputs received, timing, and
    success status for downstream ChangeSet/decision usage and audit.
    """

    tool_name: str = Field(..., min_length=1, description="Name of the tool invoked")
    inputs: dict[str, Any] = Field(
        default_factory=dict, description="Arguments sent to the tool"
    )
    output: dict[str, Any] = Field(
        default_factory=dict, description="Output returned by the tool"
    )
    duration_ms: float = Field(0.0, ge=0.0, description="Execution time (ms)")
    success: bool = Field(True, description="Whether the call succeeded")
    error: str | None = Field(
        default=None, description="Error message if the call failed"
    )
    iteration: int = Field(0, ge=0, description="Loop iteration when the call was made")

    model_config = ConfigDict(
        str_strip_whitespace=True,
        validate_assignment=True,
    )


class ToolActorContext:
    """Context carrier for a tool-calling actor execution.

    Holds project resources, sandbox root, plan metadata, resource
    bindings from the plan's projects, and the running tool call
    history for the current run.

    Parameters
    ----------
    plan_id:
        Unique identifier for the plan that owns this execution.
    phase:
        Current plan phase (e.g. ``"execute"``, ``"strategize"``).
    sandbox_root:
        Filesystem path to the sandbox root directory.
    automation_profile:
        Name of the automation profile governing this execution.
    resource_bindings:
        Mapping of slot name to resource metadata from the plan's
        projects.
    project_resources:
        Additional project resource metadata.
    metadata:
        Arbitrary extra metadata.
    """

    def __init__(
        self,
        *,
        plan_id: str,
        phase: str = "",
        sandbox_root: str = "",
        automation_profile: str = "",
        resource_bindings: dict[str, Any] | None = None,
        project_resources: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
    ) -> None:
        if not plan_id:
            raise ValueError("plan_id must not be empty")

        self._plan_id = plan_id
        self._phase = phase
        self._sandbox_root = sandbox_root
        self._automation_profile = automation_profile
        self._resource_bindings: dict[str, Any] = (
            dict(resource_bindings) if resource_bindings else {}
        )
        self._project_resources: dict[str, Any] = (
            dict(project_resources) if project_resources else {}
        )
        self._metadata: dict[str, Any] = dict(metadata) if metadata else {}
        self._tool_call_history: list[ToolCallRecord] = []

    # -- Properties -----------------------------------------------------------

    @property
    def plan_id(self) -> str:
        """Return the plan ID."""
        return self._plan_id

    @property
    def phase(self) -> str:
        """Return the current plan phase."""
        return self._phase

    @property
    def sandbox_root(self) -> str:
        """Return the sandbox root path."""
        return self._sandbox_root

    @property
    def automation_profile(self) -> str:
        """Return the automation profile name."""
        return self._automation_profile

    @property
    def resource_bindings(self) -> dict[str, Any]:
        """Return a copy of the resource bindings."""
        return dict(self._resource_bindings)

    @property
    def project_resources(self) -> dict[str, Any]:
        """Return a copy of the project resources."""
        return dict(self._project_resources)

    @property
    def metadata(self) -> dict[str, Any]:
        """Return a copy of the metadata."""
        return dict(self._metadata)

    @property
    def tool_call_history(self) -> list[ToolCallRecord]:
        """Return the tool call history (read-only copy)."""
        return list(self._tool_call_history)

    # -- Mutators -------------------------------------------------------------

    def record_tool_call(self, record: ToolCallRecord) -> None:
        """Append a tool call record to the history."""
        if not isinstance(record, ToolCallRecord):
            raise TypeError("record must be a ToolCallRecord")
        self._tool_call_history.append(record)

    def clear_history(self) -> None:
        """Clear the tool call history."""
        self._tool_call_history.clear()

    # -- Summary --------------------------------------------------------------

    def as_summary(self) -> dict[str, Any]:
        """Return a summary dict for diagnostics."""
        return {
            "plan_id": self._plan_id,
            "phase": self._phase,
            "sandbox_root": self._sandbox_root,
            "automation_profile": self._automation_profile,
            "resource_binding_count": len(self._resource_bindings),
            "project_resource_count": len(self._project_resources),
            "tool_call_count": len(self._tool_call_history),
        }
