"""Shared base of Response and AsyncResponse."""

from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, TypeVar

from ..content import Text, Thought, ToolCall
from ..formatting import Format, FormattableT
from ..messages import AssistantMessage, Message
from ..tools import FORMAT_TOOL_NAME, ToolkitT
from .finish_reason import FinishReason
from .root_response import RootResponse
from .usage import Usage

if TYPE_CHECKING:
    from ..models import Params
    from ..providers import ModelId, ProviderId

ResponseT = TypeVar("ResponseT", bound="BaseResponse[Any, Any]")


class BaseResponse(RootResponse[ToolkitT, FormattableT]):
    """The response generated by an LLM."""

    def __init__(
        self,
        *,
        raw: Any,  # noqa: ANN401
        provider_id: "ProviderId",
        model_id: "ModelId",
        provider_model_name: str,
        params: "Params",
        toolkit: ToolkitT,
        format: Format[FormattableT] | None = None,
        input_messages: Sequence[Message],
        assistant_message: AssistantMessage,
        finish_reason: FinishReason | None,
        usage: Usage | None,
    ) -> None:
        """Initialize a Response.

        Args:
            raw: The raw response from the LLM.
            provider: The provider name (e.g. "anthropic", "openai").
            model_id: The model identifier that generated the response.
            provider_model_name: Optional provider-specific model name. May include
                provider-specific additional info (like api mode in "gpt-5:responses").
            params: The params used to generate the response (or None).
            toolkit: Toolkit containing all the tools used to generate the response.
            format: The `Format` for the expected structured output format (or None).
            input_messages: The message history before the final assistant message.
            assistant_message: The final assistant message containing the response content.
            finish_reason: The reason why the LLM finished generating a response.
            usage: Token usage statistics for the response.
        """
        self.raw = raw
        self.provider_id = provider_id
        self.model_id = model_id
        self.provider_model_name = provider_model_name
        self.params = params
        self.toolkit = toolkit
        self.finish_reason = finish_reason
        self.usage = usage
        self.format = format

        # Process content in the assistant message, organizing it by type and
        # transforming ToolCalls to the special format tool into text (as calls to the
        # formatting tool are not "real" tool calls, and response.parse() expects to
        # operate on text content).
        self.texts = []
        self.tool_calls = []
        self.thoughts = []
        self.content = []
        found_format_tool = False

        for part in assistant_message.content:
            if isinstance(part, ToolCall) and part.name.startswith(FORMAT_TOOL_NAME):
                part = Text(text=part.args)
                found_format_tool = True

            self.content.append(part)
            if isinstance(part, Text):
                self.texts.append(part)
            elif isinstance(part, ToolCall):
                self.tool_calls.append(part)
            elif isinstance(part, Thought):
                self.thoughts.append(part)
            else:
                raise NotImplementedError

        if found_format_tool:
            # We create a new assistant message if we found a formatting ToolCall
            # since this indicates we've transformed the content from the original
            # NOTE: we copy over the assistant message `name` field, although in
            # practice we don't know of any providers that set the name themselves.
            assistant_message = AssistantMessage(
                content=self.content,
                name=assistant_message.name,
                provider_id=assistant_message.provider_id,
                model_id=assistant_message.model_id,
                provider_model_name=assistant_message.provider_model_name,
                raw_message=assistant_message.raw_message,
            )
        self.messages = list(input_messages) + [assistant_message]
