"""Synchronous HTTP client with retries and connection pooling."""

from __future__ import annotations

import logging
import time

from typing import TYPE_CHECKING, Any

import httpx


if TYPE_CHECKING:
    from types import TracebackType

    from pinecone._internal.http.config import HTTPConfig

from pinecone._internal.http.base import HTTPClientBase
from pinecone._internal.logging import get_logger
from pinecone.exceptions import (
    APIConnectionError,
    ConnectionError,
    TimeoutError,
)


logger = get_logger(__name__)


class HTTPClient(HTTPClientBase):
    """Synchronous HTTP client with retries and rate limiting.

    Args:
        api_key: Pinecone API key for authentication
        config: HTTP client configuration (uses defaults if not provided)
    """

    def __init__(self, api_key: str, config: HTTPConfig | None = None):
        super().__init__(api_key, config)

        # Create httpx client with connection pooling
        self._client = httpx.Client(
            http2=self._config.http2,
            timeout=self._create_timeout(),
            limits=httpx.Limits(
                max_connections=self._config.max_connections,
                max_keepalive_connections=self._config.max_keepalive_connections,
            ),
            headers=self._default_headers(),
        )

    def request(self, method: str, url: str, **kwargs: Any) -> httpx.Response:
        """Make HTTP request with automatic retries.

        Args:
            method: HTTP method (GET, POST, PUT, DELETE, PATCH)
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response

        Raises:
            APIConnectionError: If connection fails after all retries
            TimeoutError: If request times out
            APIError: If server returns an error response
        """
        last_error: Exception | None = None

        for attempt in range(self._config.max_retries):
            try:
                # Log request (with guard for hot-path performance)
                if logger._logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "HTTP request",
                        method=method,
                        url=url,
                        attempt=attempt + 1,
                    )

                # Make request
                start = time.perf_counter()
                response = self._client.request(method, url, **kwargs)
                duration = time.perf_counter() - start

                # Log response (with guard for hot-path performance)
                if logger._logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "HTTP response",
                        method=method,
                        url=url,
                        status=response.status_code,
                        duration=duration,
                    )

                # Check if should retry based on status code
                if (
                    response.status_code in self._config.retry_statuses
                    and attempt < self._config.max_retries - 1
                ):
                    wait_time = self._backoff_time(attempt, response)
                    logger.info(
                        f"Retrying after {wait_time}s",
                        status=response.status_code,
                        attempt=attempt + 1,
                    )
                    time.sleep(wait_time)
                    continue

                # Raise for error status codes
                self._raise_for_status(response)

                return response  # noqa: TRY300

            except httpx.TimeoutException as e:
                last_error = e
                if attempt < self._config.max_retries - 1:
                    wait_time = self._backoff_time(attempt)
                    logger.warning(
                        f"Request timed out, retrying after {wait_time}s",
                        error=str(e),
                        attempt=attempt + 1,
                    )
                    time.sleep(wait_time)
                    continue
                logger.exception(
                    "Request timed out after all retries",
                    timeout=self._config.timeout,
                    attempts=attempt + 1,
                )
                raise TimeoutError(timeout=self._resolve_timeout_value(e)) from e

            except httpx.ConnectError as e:
                last_error = e
                if attempt < self._config.max_retries - 1:
                    wait_time = self._backoff_time(attempt)
                    logger.warning(
                        f"Connection failed, retrying after {wait_time}s",
                        error=str(e),
                        attempt=attempt + 1,
                    )
                    time.sleep(wait_time)
                    continue
                logger.exception(
                    "Connection failed after all retries",
                    url=url,
                    attempts=attempt + 1,
                )
                raise ConnectionError(f"Failed to connect to {url}") from e

            except httpx.RequestError as e:
                last_error = e
                if attempt < self._config.max_retries - 1:
                    wait_time = self._backoff_time(attempt)
                    logger.warning(
                        f"Request failed, retrying after {wait_time}s",
                        error=str(e),
                        attempt=attempt + 1,
                    )
                    time.sleep(wait_time)
                    continue

        # All retries exhausted
        logger.error(
            "Request failed after all retries",
            method=method,
            url=url,
            attempts=self._config.max_retries,
        )
        raise APIConnectionError(
            f"Request failed after {self._config.max_retries} attempts"
        ) from last_error

    def get(self, url: str, **kwargs: Any) -> httpx.Response:
        """Make GET request.

        Args:
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response
        """
        return self.request("GET", url, **kwargs)

    def post(self, url: str, **kwargs: Any) -> httpx.Response:
        """Make POST request.

        Args:
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response
        """
        return self.request("POST", url, **kwargs)

    def put(self, url: str, **kwargs: Any) -> httpx.Response:
        """Make PUT request.

        Args:
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response
        """
        return self.request("PUT", url, **kwargs)

    def delete(self, url: str, **kwargs: Any) -> httpx.Response:
        """Make DELETE request.

        Args:
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response
        """
        return self.request("DELETE", url, **kwargs)

    def patch(self, url: str, **kwargs: Any) -> httpx.Response:
        """Make PATCH request.

        Args:
            url: URL to request
            **kwargs: Additional arguments passed to httpx

        Returns:
            HTTP response
        """
        return self.request("PATCH", url, **kwargs)

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

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

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        """Exit context manager and clean up resources."""
        self.close()
