"""APIResponse wrapper for raw HTTP response access."""

from __future__ import annotations

from typing import Generic, Type, TypeVar

import httpx
import pydantic

from rulebook._base_client import _model_has_data_field, _parse_model

_T = TypeVar("_T")


class APIResponse(Generic[_T]):
    """Wrapper around an HTTP response that provides raw access and lazy parsing.

    Usage::

        raw = client.exchanges.with_raw_response.retrieve("NYSE")
        raw.status_code    # 200
        raw.headers        # httpx.Headers
        detail = raw.parse()  # ExchangeDetail
    """

    _response: httpx.Response
    _cast_to: Type[_T]

    def __init__(self, *, response: httpx.Response, cast_to: Type[_T]) -> None:
        self._response = response
        self._cast_to = cast_to

    @property
    def status_code(self) -> int:
        return self._response.status_code

    @property
    def headers(self) -> httpx.Headers:
        return self._response.headers

    @property
    def http_request(self) -> httpx.Request:
        return self._response.request

    @property
    def http_response(self) -> httpx.Response:
        return self._response

    def parse(self) -> _T:
        """Parse the response body into the target type."""
        body = self._response.json()

        # Models with a ``data`` field (e.g. PaginatedResponse) receive the
        # full body so they can capture pagination metadata alongside items.
        if _model_has_data_field(self._cast_to):
            return _parse_model(self._cast_to, body)

        # Unwrap envelope
        if isinstance(body, dict) and "data" in body:
            data = body["data"]
            meta = body.get("meta")
            if isinstance(data, dict) and isinstance(meta, dict):
                for key, value in meta.items():
                    if key not in data:
                        data[key] = value
        else:
            data = body

        return _parse_model(self._cast_to, data)

    def __repr__(self) -> str:
        return f"APIResponse(status_code={self.status_code}, cast_to={self._cast_to.__name__})"
