"""Agent result handling for SDK.

This module defines the AgentResult class which encapsulates the complete response from an agent's processing cycle.
"""

from collections.abc import Sequence
from dataclasses import dataclass
from typing import Any, cast

from pydantic import BaseModel

from ..experimental.checkpoint import Checkpoint
from ..interrupt import Interrupt
from ..telemetry.metrics import EventLoopMetrics
from ..types.content import Message
from ..types.streaming import StopReason


@dataclass
class AgentResult:
    """Represents the last result of invoking an agent with a prompt.

    Attributes:
        stop_reason: The reason why the agent's processing stopped.
        message: The last message generated by the agent.
        metrics: Performance metrics collected during processing.
        state: Additional state information from the event loop.
        interrupts: List of interrupts if raised by user.
        structured_output: Parsed structured output when structured_output_model was specified.
        checkpoint: Checkpoint captured when the agent paused for durable execution.
            Populated only when stop_reason == "checkpoint". See
            strands.experimental.checkpoint for usage.
    """

    stop_reason: StopReason
    message: Message
    metrics: EventLoopMetrics
    state: Any
    interrupts: Sequence[Interrupt] | None = None
    structured_output: BaseModel | None = None
    checkpoint: Checkpoint | None = None

    @property
    def context_size(self) -> int | None:
        """Most recent context size in tokens from the last LLM call.

        Returns:
            The input token count from the most recent cycle, or None if no data is available.
        """
        return self.metrics.latest_context_size

    @property
    def projected_context_size(self) -> int | None:
        """Projected context size for the next model call.

        Returns:
            The projected token count (inputTokens + outputTokens), or None if no data is available.
        """
        return self.metrics.projected_context_size

    def __str__(self) -> str:
        """Return a string representation of the agent result.

        Priority order:
        1. Interrupts (if present) → stringified list of interrupt dicts
        2. Structured output (if present) → JSON string
        3. Text content from message → concatenated text blocks

        Returns:
            String representation based on the priority order above.
        """
        if self.interrupts:
            return str([interrupt.to_dict() for interrupt in self.interrupts])

        if self.structured_output:
            return self.structured_output.model_dump_json()

        content_array = self.message.get("content", [])
        result = ""
        for item in content_array:
            if isinstance(item, dict):
                if "text" in item:
                    result += item.get("text", "") + "\n"
                elif "citationsContent" in item:
                    citations_block = item["citationsContent"]
                    if "content" in citations_block:
                        for content in citations_block["content"]:
                            if isinstance(content, dict) and "text" in content:
                                result += content.get("text", "") + "\n"

        return result

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "AgentResult":
        """Rehydrate an AgentResult from persisted JSON.

        Args:
            data: Dictionary containing the serialized AgentResult data
        Returns:
            AgentResult instance
        Raises:
            TypeError: If the data format is invalid
        """
        if data.get("type") != "agent_result":
            raise TypeError(f"AgentResult.from_dict: unexpected type {data.get('type')!r}")

        message = cast(Message, data.get("message"))
        stop_reason = cast(StopReason, data.get("stop_reason"))
        checkpoint_data = data.get("checkpoint")
        checkpoint = Checkpoint.from_dict(checkpoint_data) if checkpoint_data else None

        return cls(
            message=message,
            stop_reason=stop_reason,
            metrics=EventLoopMetrics(),
            state={},
            checkpoint=checkpoint,
        )

    def to_dict(self) -> dict[str, Any]:
        """Convert this AgentResult to JSON-serializable dictionary.

        Returns:
            Dictionary containing serialized AgentResult data
        """
        return {
            "type": "agent_result",
            "message": self.message,
            "stop_reason": self.stop_reason,
            "checkpoint": self.checkpoint.to_dict() if self.checkpoint else None,
        }
