from typing import Any

import httpx

from ..exceptions import DeliveryError, RetryableDeliveryError
from .retry import with_retry

DEFAULT_TIMEOUT = 5.0


@with_retry(exceptions=(httpx.HTTPError, RetryableDeliveryError))
def post(
    url: str,
    *,
    json: dict[str, Any],
    timeout: float = DEFAULT_TIMEOUT,
) -> httpx.Response:
    """Send a POST request to the endpoint (synchronous).

    Args:
        url: Endpoint URL.
        json: Message payload.
        timeout: HTTP request timeout (seconds)

    Returns:
        HTTP response.
    """
    response: httpx.Response = httpx.post(url, json=json, timeout=timeout)
    _check_response(response)
    return response


@with_retry(exceptions=(httpx.HTTPError, RetryableDeliveryError))
async def post_async(
    url: str,
    *,
    json: dict[str, Any],
    timeout: float = DEFAULT_TIMEOUT,
) -> httpx.Response:
    """Send a POST request to the endpoint (asynchronous).

    Args:
        url: Endpoint URL.
        json: Message payload.
        timeout: HTTP request timeout (seconds)

    Returns:
        HTTP response.
    """
    async with httpx.AsyncClient() as client:
        response: httpx.Response = await client.post(url, json=json, timeout=timeout)
        _check_response(response)
        return response


def _check_response(response: httpx.Response) -> None:
    """Check the response status code.

    Raises:
        RetryableDeliveryError: Response status code >= 500.
        DeliveryError: Response status code 4xx.
    """
    status: int = response.status_code
    if status < 400:
        return

    message: str = f"Delivery failed with status code {status}: {response.text}."
    if status >= 500:
        raise RetryableDeliveryError(message)

    raise DeliveryError(message)
