"""WorkBoard API client with security hardening.

This module provides a secure async HTTP client for the WorkBoard API.
All requests go through this client to ensure consistent security practices.
"""

import logging
from typing import Any

import httpx

from .config import get_config
from .errors import (
    NotFoundError,
    PermissionDeniedError,
    RateLimitError,
    WorkBoardApiError,
)

logger = logging.getLogger(__name__)

# Response size limit to prevent memory exhaustion (10MB)
MAX_RESPONSE_SIZE = 10 * 1024 * 1024

# Request timeout in seconds
REQUEST_TIMEOUT = 30.0


class WorkBoardClient:
    """Async HTTP client for WorkBoard API.

    Security features:
    - Hardcoded base URL (prevents SSRF)
    - Token passed via auth header (not URL)
    - TLS certificate validation (httpx default)
    - Request timeout enforcement
    - Response size limits
    """

    def __init__(self) -> None:
        """Initialize the WorkBoard client."""
        self._config = get_config()
        self._client: httpx.AsyncClient | None = None

    async def _get_client(self) -> httpx.AsyncClient:
        """Get or create the async HTTP client."""
        if self._client is None:
            self._client = httpx.AsyncClient(
                base_url=self._config.api_base_url,
                headers={
                    "Authorization": f"Bearer {self._config.token}",
                    "Content-Type": "application/json",
                },
                timeout=httpx.Timeout(REQUEST_TIMEOUT),
                verify=True,
            )
        return self._client

    async def close(self) -> None:
        """Close the HTTP client."""
        if self._client is not None:
            await self._client.aclose()
            self._client = None

    async def _request(
        self,
        method: str,
        path: str,
        params: dict[str, Any] | None = None,
        json_data: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make an API request with error handling.

        Args:
            method: HTTP method (GET, POST, PUT, PATCH, DELETE)
            path: API path (e.g., /user)
            params: Query parameters
            json_data: JSON body data

        Returns:
            API response data

        Raises:
            WorkBoardApiError: On API errors
            RateLimitError: On rate limiting
            PermissionDeniedError: On authorization failures
        """
        client = await self._get_client()

        logger.debug("API request: %s %s", method, path)

        try:
            response = await client.request(
                method=method,
                url=path,
                params=params,
                json=json_data,
            )
        except httpx.TimeoutException as e:
            raise WorkBoardApiError(0, f"Request timeout: {e}") from e
        except httpx.RequestError as e:
            raise WorkBoardApiError(0, f"Request failed: {e}") from e

        # Check response size before parsing
        content_length = response.headers.get("content-length")
        if content_length and int(content_length) > MAX_RESPONSE_SIZE:
            raise WorkBoardApiError(0, "Response too large")

        # Parse response
        try:
            data = response.json()
        except ValueError as e:
            raise WorkBoardApiError(
                response.status_code, f"Invalid JSON response: {e}"
            ) from e

        # Handle error responses
        if not response.is_success:
            self._handle_error_response(response.status_code, data)

        return data  # type: ignore[no-any-return]

    def _handle_error_response(
        self, status_code: int, data: dict[str, Any]
    ) -> None:
        """Handle error responses from the API.

        Args:
            status_code: HTTP status code
            data: Response data

        Raises:
            Various UserError subclasses based on error type
        """
        error_msg = data.get("message", "Unknown error")
        if isinstance(error_msg, dict):
            error_msg = str(error_msg)

        if status_code == 401:
            raise PermissionDeniedError("Valid API token")
        if status_code == 403:
            raise PermissionDeniedError("Required permission scope")
        if status_code == 404:
            raise NotFoundError("Resource", str(error_msg))
        if status_code == 429:
            retry_after = data.get("retry_after")
            raise RateLimitError(retry_after)

        raise WorkBoardApiError(status_code, str(error_msg))

    # Convenience methods for HTTP verbs

    async def get(
        self, path: str, params: dict[str, Any] | None = None
    ) -> dict[str, Any]:
        """Make a GET request."""
        return await self._request("GET", path, params=params)

    async def post(
        self,
        path: str,
        json_data: dict[str, Any] | None = None,
        params: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make a POST request."""
        return await self._request("POST", path, params=params, json_data=json_data)

    async def put(
        self,
        path: str,
        json_data: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make a PUT request."""
        return await self._request("PUT", path, json_data=json_data)

    async def patch(
        self,
        path: str,
        json_data: dict[str, Any] | None = None,
    ) -> dict[str, Any]:
        """Make a PATCH request."""
        return await self._request("PATCH", path, json_data=json_data)

    async def delete(self, path: str) -> dict[str, Any]:
        """Make a DELETE request."""
        return await self._request("DELETE", path)


# Global client instance
_client: WorkBoardClient | None = None


def get_client() -> WorkBoardClient:
    """Get the global WorkBoard client instance."""
    global _client
    if _client is None:
        _client = WorkBoardClient()
    return _client
