# Copyright 2025 Tenro.ai
# SPDX-License-Identifier: Apache-2.0

"""Provider response wrapper for LLM provider mocking.

This module provides the ProviderResponse class, which acts as a "duck type"
for any LLM provider response object (OpenAI, Anthropic, Gemini, custom providers).
Returned by ProviderSchemaFactory for testing and mocking purposes.
"""

from __future__ import annotations

from typing import Any


class ProviderResponse(dict[str, Any]):
    """Hybrid dict/attribute access wrapper for LLM provider responses.

    Acts as a "duck type" for any LLM provider response object (OpenAI, Anthropic,
    Gemini, custom providers), supporting both modern attribute access
    (response.choices[0]) and legacy dictionary access (response["choices"][0]).

    Returned by ProviderSchemaFactory.create_response() for testing and mocking.

    This approach provides:
    - Zero dependencies on provider SDKs (lightweight)
    - Hybrid access patterns (forgiving for legacy code)
    - Forward compatibility (new provider fields work immediately)
    - Partial mocking (only include fields you test)

    Examples:
        >>> response = ProviderResponse({
        ...     "choices": [{"message": {"content": "Hello!"}}]
        ... })
        >>> response.choices[0].message.content  # Attribute access
        'Hello!'
        >>> response["choices"][0]["message"]["content"]  # Dict access
        'Hello!'
    """

    def __init__(self, data: dict[str, Any]) -> None:
        """Initialize with recursive wrapping of nested structures.

        Args:
            `data`: The dictionary to wrap. Nested dicts and lists are
                automatically wrapped to support attribute access at all levels.
        """
        super().__init__(data)
        for key, value in data.items():
            # Recursively wrap nested dicts
            if isinstance(value, dict):
                self[key] = ProviderResponse(value)
            # Recursively wrap lists containing dicts
            elif isinstance(value, list):
                self[key] = [
                    ProviderResponse(item) if isinstance(item, dict) else item for item in value
                ]
            else:
                self[key] = value

    def __getattribute__(self, name: str) -> Any:
        """Override attribute access to prioritize dict keys over methods.

        This ensures that user data keys (like 'items', 'keys', 'values') take
        precedence over built-in dict methods.

        Args:
            `name`: The attribute name to access.
        Returns:
            The value associated with the key, or the attribute if not a key.

        Raises:
            AttributeError: If neither key nor attribute exists.
        """
        # Get the underlying dict to check if name is a key
        # Use object.__getattribute__ to avoid infinite recursion
        try:
            _dict = object.__getattribute__(self, "__class__").__bases__[0].__getitem__(self, name)
            return _dict
        except (KeyError, IndexError):
            # Not a dict key, fall back to normal attribute access
            return object.__getattribute__(self, name)

    def __setattr__(self, name: str, value: Any) -> None:
        """Allow attribute assignment (obj.attr = value).

        Args:
            `name`: The attribute name to set.
            `value`: The value to assign.
        """
        self[name] = value

    def model_dump(self) -> dict[str, Any]:
        """Mock Pydantic's model_dump() method for compatibility.

        Returns:
            Plain dictionary representation.
        """
        return dict(self)

    def to_dict(self) -> dict[str, Any]:
        """Convert to plain dictionary.

        Returns:
            Plain dictionary representation.
        """
        return dict(self)
