"""HTTP transport with retry logic using httpx."""

from __future__ import annotations

import random
from dataclasses import dataclass
from typing import Any

import httpx

from .errors import HttpError, is_retryable_status
from .errors import TimeoutError as UndetectaTimeoutError

DEFAULT_MAX_RETRIES = 3
BASE_DELAY_MS = 1000
MAX_DELAY_MS = 10000


@dataclass
class TransportConfig:
    """Configuration for HTTP transport."""

    base_url: str
    api_key: str
    timeout: int
    max_retries: int
    user_agent: str | None = None


def calculate_delay(attempt: int) -> float:
    """Calculate delay with exponential backoff and jitter.

    Args:
        attempt: Attempt number (0-indexed)

    Returns:
        Delay in seconds
    """
    exponential_delay = min(BASE_DELAY_MS * 2**attempt, MAX_DELAY_MS)
    jitter = random.random() * 0.3 * exponential_delay  # Up to 30% jitter
    return (exponential_delay + jitter) / 1000  # Convert to seconds


def build_headers(
    api_key: str,
    user_agent: str | None = None,
    additional_headers: dict[str, str] | None = None,
) -> dict[str, str]:
    """Build headers for a request.

    Args:
        api_key: API key for authentication
        user_agent: Custom user agent string
        additional_headers: Additional headers to include

    Returns:
        Dictionary of headers
    """
    headers: dict[str, str] = {
        "Content-Type": "application/json",
        "x-api-key": api_key,
    }

    if user_agent:
        headers["User-Agent"] = user_agent

    if additional_headers:
        headers.update(additional_headers)

    return headers


def build_url(base_url: str, path: str) -> str:
    """Build a full URL from base URL and path.

    Args:
        base_url: Base URL
        path: Path to append

    Returns:
        Full URL
    """
    return f"{base_url.rstrip('/')}/{path.lstrip('/')}"


async def request_with_retry(
    client: httpx.AsyncClient,
    method: str,
    url: str,
    config: TransportConfig,
    json_data: dict[str, Any] | None = None,
) -> Any:
    """Make an HTTP request with retry logic.

    Args:
        client: httpx async client
        method: HTTP method
        url: Request URL
        config: Transport configuration
        json_data: JSON body for POST requests

    Returns:
        Response data

    Raises:
        HttpError: On HTTP errors
        UndetectaTimeoutError: On timeout
    """
    headers = build_headers(config.api_key, config.user_agent)

    for attempt in range(config.max_retries + 1):
        try:
            response = await client.request(
                method=method,
                url=url,
                headers=headers,
                json=json_data if method.upper() == "POST" else None,
                timeout=config.timeout / 1000,  # Convert to seconds
            )

            # Handle non-OK responses
            if not response.is_success:
                error = HttpError(
                    response.reason_phrase or "Request failed",
                    response.status_code,
                    response.json() if response.headers.get("content-type", "").startswith("application/json") else None,
                )

                # Check if retryable
                if attempt < config.max_retries and is_retryable_status(response.status_code):
                    delay = calculate_delay(attempt)
                    await _log_retry(attempt + 1, error)
                    await _sleep(delay)
                    continue

                raise error

            # Parse response
            data = response.json()

            # Handle wrapped response format
            if isinstance(data, dict) and "success" in data:
                if data["success"] is False:
                    error_obj = data.get("error", {})
                    if isinstance(error_obj, dict):
                        message = str(error_obj.get("message", "Request failed"))
                    else:
                        message = "Request failed"
                    raise HttpError(
                        message,
                        response.status_code,
                        data,
                    )
                data_result = data.get("data", data)
                return data_result  # type: ignore[return-value]

            return data  # type: ignore[return-value]

        except httpx.TimeoutException:
            error = UndetectaTimeoutError(f"Request timeout after {config.timeout}ms")

            if attempt < config.max_retries:
                delay = calculate_delay(attempt)
                await _log_retry(attempt + 1, error)
                await _sleep(delay)
                continue

            raise error

        except httpx.NetworkError as e:
            error = HttpError(f"Network error: {e}", 0, None)

            if attempt < config.max_retries:
                delay = calculate_delay(attempt)
                await _log_retry(attempt + 1, error)
                await _sleep(delay)
                continue

            raise error

        except HttpError:
            # Re-raise HTTP errors that weren't retried
            raise

        except Exception as e:
            # Unknown error
            error = HttpError(f"Unexpected error: {e}", 0, None)
            raise error

    # Should never reach here
    raise HttpError("Max retries exceeded", 0, None)


async def _sleep(seconds: float) -> None:
    """Async sleep.

    Args:
        seconds: Seconds to sleep
    """
    import asyncio

    await asyncio.sleep(seconds)


async def _log_retry(attempt: int, error: Exception) -> None:
    """Log retry attempt.

    Args:
        attempt: Attempt number
        error: Error that triggered retry
    """
    import warnings

    if isinstance(error, HttpError) and error.status_code:
        warnings.warn(f"Retrying request (attempt {attempt}): HTTP {error.status_code}")
    else:
        warnings.warn(f"Retrying request (attempt {attempt}): {error}")
