"""Main Concentriq LS API client."""

from typing import Any, Optional

import httpx

from concentriq.auth.base import AuthHandler
from concentriq.exceptions import (
    APIError,
    AuthenticationError,
    AuthorizationError,
    NotFoundError,
    ServerError,
    ValidationError,
)


class ConcentriqClient:
    """Main client for interacting with the Concentriq LS API.

    Args:
        base_url: The base URL of the Concentriq API (e.g., https://app.concentriq.com)
        auth: Authentication handler for API requests
        timeout: Request timeout in seconds (default: 30.0)

    Raises:
        ValueError: If base_url is empty or auth is None

    Example:
        >>> from concentriq import ConcentriqClient
        >>> from concentriq.auth import ApiKeyAuth
        >>>
        >>> client = ConcentriqClient(
        ...     base_url="https://app.concentriq.com",
        ...     auth=ApiKeyAuth(api_key="your-api-key")
        ... )
        >>>
        >>> # Use the client...
        >>> client.close()

        Or use as context manager:

        >>> with ConcentriqClient(base_url="...", auth=auth) as client:
        ...     # Use the client
        ...     pass
    """

    def __init__(
        self,
        base_url: str,
        auth: Optional[AuthHandler],
        timeout: float = 30.0,
    ):
        if not base_url:
            raise ValueError("base_url is required")
        if auth is None:
            raise ValueError("auth is required")

        self.base_url = base_url.rstrip("/")
        self.auth = auth
        self.timeout = timeout

        # Create httpx client
        self._client = httpx.Client(timeout=timeout)

        # API version namespaces (lazy-loaded)
        self._v1: Optional[Any] = None
        self._v2: Optional[Any] = None
        self._v3: Optional[Any] = None

    @property
    def v1(self) -> Any:
        """Access V1 API namespace (/api/*)."""
        if self._v1 is None:
            from concentriq.api.v1 import V1API

            self._v1 = V1API(self)
        return self._v1

    @property
    def v2(self) -> Any:
        """Access V2 API namespace (/api/v2/*)."""
        if self._v2 is None:
            from concentriq.api.v2 import V2API

            self._v2 = V2API(self)
        return self._v2

    @property
    def v3(self) -> Any:
        """Access V3 API namespace (/api/v3/*)."""
        if self._v3 is None:
            from concentriq.api.v3 import V3API

            self._v3 = V3API(self)
        return self._v3

    def _request(
        self,
        method: str,
        path: str,
        **kwargs: Any,
    ) -> httpx.Response:
        """Make an HTTP request to the API.

        Args:
            method: HTTP method (GET, POST, PUT, DELETE, etc.)
            path: API path (relative to base_url)
            **kwargs: Additional arguments to pass to httpx.request()

        Returns:
            httpx.Response object

        Raises:
            AuthenticationError: On 401 status
            AuthorizationError: On 403 status
            NotFoundError: On 404 status
            ValidationError: On 400 or 422 status
            ServerError: On 5xx status
            APIError: On other error status codes
        """
        # Build full URL
        url = f"{self.base_url}{path}"

        # Add auth headers
        headers = kwargs.pop("headers", {})
        auth_headers = self.auth.get_headers()
        headers.update(auth_headers)

        # Make request
        response = self._client.request(
            method=method,
            url=url,
            headers=headers,
            **kwargs,
        )

        # Handle errors
        self._handle_error_response(response)

        return response

    def _handle_error_response(self, response: httpx.Response) -> None:
        """Handle error responses and raise appropriate exceptions.

        Args:
            response: The HTTP response to check

        Raises:
            AuthenticationError: On 401 status
            AuthorizationError: On 403 status
            NotFoundError: On 404 status
            ValidationError: On 400 or 422 status
            ServerError: On 5xx status
            APIError: On other error status codes
        """
        if response.is_success:
            return

        status_code = response.status_code

        # Redirects (3xx) are not errors
        if 300 <= status_code < 400:
            return

        # Try to get error details from response
        try:
            error_data = response.json()
        except Exception:
            error_data = {}

        error_message = error_data.get("error", f"HTTP {status_code}")

        # Map status codes to exceptions
        if status_code == 401:
            raise AuthenticationError(f"[{status_code}] {error_message}")
        elif status_code == 403:
            raise AuthorizationError(f"[{status_code}] {error_message}")
        elif status_code == 404:
            raise NotFoundError(f"[{status_code}] {error_message}")
        elif status_code in (400, 422):
            raise ValidationError(f"[{status_code}] {error_message}")
        elif 500 <= status_code < 600:
            raise ServerError(f"[{status_code}] {error_message}")
        else:
            raise APIError(
                message=error_message,
                status_code=status_code,
                response_data=error_data,
            )

    def close(self) -> None:
        """Close the HTTP client and release resources."""
        self._client.close()

    def __enter__(self) -> "ConcentriqClient":
        """Enter context manager."""
        return self

    def __exit__(self, *args: Any) -> None:
        """Exit context manager and close client."""
        self.close()
