"""Stateless HTTP wrapper for Toyota Connected Services API."""

from __future__ import annotations

import hashlib
import hmac
from typing import Any
from uuid import uuid4

import httpx

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

API_KEY = "tTZipv6liF74PwMfk9Ed68AQ0bISswwf3iHQdqcF"
API_BASE_URL = "https://ctpa-oneapi.tceu-ctp-prd.toyotaconnectedeurope.io"
CLIENT_VERSION = "2.14.0"


# ---------------------------------------------------------------------------
# Exceptions
# ---------------------------------------------------------------------------


class ToyotaApiError(Exception):
    """Raised when the Toyota API returns a non-2xx response."""

    def __init__(self, status_code: int, message: str) -> None:
        super().__init__(f"Toyota API error {status_code}: {message}")
        self.status_code = status_code
        self.message = message


# ---------------------------------------------------------------------------
# HMAC helper
# ---------------------------------------------------------------------------


def _hmac_sha256(key: str, message: str) -> str:
    """Compute HMAC-SHA256 hex digest.

    Args:
        key: HMAC key string.
        message: Message string to sign.

    Returns:
        Hex-encoded HMAC-SHA256 digest.

    """
    return hmac.new(key.encode(), message.encode(), hashlib.sha256).hexdigest()


# ---------------------------------------------------------------------------
# ToyotaClient
# ---------------------------------------------------------------------------


class ToyotaClient:
    """Stateless HTTP client for the Toyota Connected Services API.

    Builds all required headers automatically on each request. Owns no token
    state — credentials are supplied once at construction time.

    """

    def __init__(
        self,
        access_token: str,
        uuid: str,
        brand: str = "T",
        timeout: int = 60,
    ) -> None:
        """Initialise the client.

        Args:
            access_token: OAuth2 access token.
            uuid: User UUID extracted from id_token.
            brand: Vehicle brand — "T" for Toyota (default), "L" for Lexus.
            timeout: HTTP request timeout in seconds.

        """
        self._access_token = access_token
        self._uuid = uuid
        self._brand = brand
        self._timeout = timeout

    # ------------------------------------------------------------------
    # Header construction
    # ------------------------------------------------------------------

    def _prepare_headers(self, vin: str | None = None) -> dict[str, str]:
        """Build the full header dict for an API request.

        Args:
            vin: Vehicle Identification Number. When provided, adds a ``vin``
                header required by per-vehicle endpoints.

        Returns:
            Dict of HTTP headers.

        """
        headers: dict[str, str] = {
            "x-api-key": API_KEY,
            "API_KEY": API_KEY,
            "x-guid": self._uuid,
            "guid": self._uuid,
            "x-client-ref": _hmac_sha256(CLIENT_VERSION, self._uuid),
            "x-correlationid": str(uuid4()),
            "x-appversion": CLIENT_VERSION,
            "x-channel": "ONEAPP",
            "x-brand": self._brand,
            "x-region": "EU",
            "authorization": f"Bearer {self._access_token}",
            "user-agent": "okhttp/4.10.0",
        }

        if self._brand == "L":
            headers["x-appbrand"] = "L"
            headers["brand"] = "L"

        if vin is not None:
            headers["vin"] = vin

        return headers

    # ------------------------------------------------------------------
    # HTTP methods
    # ------------------------------------------------------------------

    async def get(
        self,
        endpoint: str,
        vin: str | None = None,
        params: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Send a GET request to the API.

        Args:
            endpoint: Path relative to ``API_BASE_URL``.
            vin: Optional VIN to include as a request header.
            params: Optional query parameters.

        Returns:
            Parsed JSON response body.

        Raises:
            ToyotaApiError: On any non-2xx response.

        """
        async with httpx.AsyncClient(timeout=self._timeout) as client:
            response = await client.get(
                f"{API_BASE_URL}{endpoint}",
                headers=self._prepare_headers(vin=vin),
                params=params,
            )
        return self._handle_response(response)

    async def post(
        self,
        endpoint: str,
        vin: str | None = None,
        body: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Send a POST request to the API.

        Args:
            endpoint: Path relative to ``API_BASE_URL``.
            vin: Optional VIN to include as a request header.
            body: Optional JSON request body.

        Returns:
            Parsed JSON response body.

        Raises:
            ToyotaApiError: On any non-2xx response.

        """
        async with httpx.AsyncClient(timeout=self._timeout) as client:
            response = await client.post(
                f"{API_BASE_URL}{endpoint}",
                headers=self._prepare_headers(vin=vin),
                json=body,
            )
        return self._handle_response(response)

    async def put(
        self,
        endpoint: str,
        vin: str | None = None,
        body: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Send a PUT request to the API.

        Args:
            endpoint: Path relative to ``API_BASE_URL``.
            vin: Optional VIN to include as a request header.
            body: Optional JSON request body.

        Returns:
            Parsed JSON response body.

        Raises:
            ToyotaApiError: On any non-2xx response.

        """
        async with httpx.AsyncClient(timeout=self._timeout) as client:
            response = await client.put(
                f"{API_BASE_URL}{endpoint}",
                headers=self._prepare_headers(vin=vin),
                json=body,
            )
        return self._handle_response(response)

    # ------------------------------------------------------------------
    # Response handling
    # ------------------------------------------------------------------

    @staticmethod
    def _handle_response(response: httpx.Response) -> dict[str, Any]:
        """Parse response and raise on error.

        Args:
            response: httpx response object.

        Returns:
            Parsed JSON body as a dict.

        Raises:
            ToyotaApiError: If status code is not 2xx.

        """
        if not response.is_success:
            raise ToyotaApiError(response.status_code, response.text)
        return response.json()
