"""HTTP client wrapper for the Contiss API."""

import time
from typing import Any, Dict, Optional, Union

import httpx

from .exceptions import (
    APIError,
    AuthenticationError,
    ConflictError,
    ConnectionError,
    NotFoundError,
    RateLimitError,
    TimeoutError,
    ValidationError,
)


class HTTPClient:
    """HTTP client for making requests to the Contiss API."""

    def __init__(
        self,
        base_url: str,
        api_key: Optional[str] = None,
        token: Optional[str] = None,
        timeout: float = 30.0,
        max_retries: int = 3,
    ) -> None:
        """Initialize the HTTP client.

        Args:
            base_url: Base URL for the API
            api_key: API key for authentication
            token: JWT token for authentication
            timeout: Request timeout in seconds
            max_retries: Maximum number of retries for failed requests
        """
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.token = token
        self.timeout = timeout
        self.max_retries = max_retries

        # Build headers
        headers = {"Content-Type": "application/json"}
        if api_key:
            headers["Authorization"] = f"Bearer {api_key}"
        elif token:
            headers["Authorization"] = f"Bearer {token}"

        self.client = httpx.Client(
            base_url=self.base_url, headers=headers, timeout=timeout, follow_redirects=True
        )

    def _handle_error(self, response: httpx.Response) -> None:
        """Handle error responses.

        Args:
            response: HTTP response object

        Raises:
            Various Contiss exceptions based on status code
        """
        try:
            data = response.json()
            message = data.get("message", response.text)
        except Exception:
            message = response.text

        if response.status_code == 400:
            raise ValidationError(f"Bad request: {message}")
        elif response.status_code == 401:
            raise AuthenticationError(f"Authentication failed: {message}")
        elif response.status_code == 404:
            raise NotFoundError(f"Resource not found: {message}")
        elif response.status_code == 409:
            raise ConflictError(f"Conflict: {message}")
        elif response.status_code == 429:
            retry_after = response.headers.get("Retry-After")
            retry_seconds = int(retry_after) if retry_after else None
            raise RateLimitError(f"Rate limit exceeded: {message}", retry_after=retry_seconds)
        else:
            raise APIError(message, status_code=response.status_code, response=data if "data" in locals() else {})

    def _request_with_retry(
        self,
        method: str,
        path: str,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """Make a request with retry logic.

        Args:
            method: HTTP method
            path: API path
            json: JSON body data
            params: Query parameters

        Returns:
            Response data as dictionary

        Raises:
            Various Contiss exceptions
        """
        last_exception: Optional[Exception] = None

        for attempt in range(self.max_retries):
            try:
                response = self.client.request(method, path, json=json, params=params)

                # Handle error responses
                if response.status_code >= 400:
                    self._handle_error(response)

                # Return JSON response
                if response.status_code == 204:  # No content
                    return {}
                return response.json()

            except httpx.TimeoutException as e:
                last_exception = TimeoutError(f"Request timed out after {self.timeout}s")
                if attempt < self.max_retries - 1:
                    time.sleep(2**attempt)  # Exponential backoff
                    continue
                raise last_exception from e

            except httpx.ConnectError as e:
                last_exception = ConnectionError(f"Failed to connect to {self.base_url}")
                if attempt < self.max_retries - 1:
                    time.sleep(2**attempt)
                    continue
                raise last_exception from e

            except (ValidationError, AuthenticationError, NotFoundError, ConflictError):
                # Don't retry these errors
                raise

            except RateLimitError as e:
                # Retry with backoff based on Retry-After header
                if attempt < self.max_retries - 1 and e.retry_after:
                    time.sleep(e.retry_after)
                    continue
                raise

        # If we exhausted retries
        if last_exception:
            raise last_exception

        raise APIError("Request failed after retries", status_code=500)

    def get(
        self, path: str, params: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Make a GET request.

        Args:
            path: API path
            params: Query parameters

        Returns:
            Response data
        """
        return self._request_with_retry("GET", path, params=params)

    def post(
        self,
        path: str,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """Make a POST request.

        Args:
            path: API path
            json: JSON body data
            params: Query parameters

        Returns:
            Response data
        """
        return self._request_with_retry("POST", path, json=json, params=params)

    def put(
        self,
        path: str,
        json: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """Make a PUT request.

        Args:
            path: API path
            json: JSON body data
            params: Query parameters

        Returns:
            Response data
        """
        return self._request_with_retry("PUT", path, json=json, params=params)

    def delete(
        self, path: str, params: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Make a DELETE request.

        Args:
            path: API path
            params: Query parameters

        Returns:
            Response data
        """
        return self._request_with_retry("DELETE", path, params=params)

    def close(self) -> None:
        """Close the HTTP client."""
        self.client.close()

    def __enter__(self) -> "HTTPClient":
        """Context manager entry."""
        return self

    def __exit__(self, *args: Any) -> None:
        """Context manager exit."""
        self.close()
