"""Shared HTTP retry helper for GRC API push exporters."""

from __future__ import annotations

import asyncio
import random
from typing import Any

import httpx

from complyform.core.errors import (
    ExportAPIAuthError,
    ExportAPIError,
    ExportAPIRateLimitError,
)

MAX_RETRIES = 3
BASE_DELAY = 1.0
REQUEST_TIMEOUT = 30.0

_RETRYABLE_STATUSES = frozenset({429, 500, 502, 503})
_NO_RETRY_STATUSES = frozenset({401, 403, 404})


async def grc_request(
    client: httpx.AsyncClient,
    method: str,
    url: str,
    *,
    platform: str = "grc",
    **kwargs: Any,
) -> httpx.Response:
    """Execute an HTTP request with retry logic for GRC APIs.

    Retries on 429, 500, 502, 503 with exponential backoff + jitter.
    Raises immediately on 401, 403, 404.
    """
    for attempt in range(MAX_RETRIES):
        try:
            response = await client.request(
                method,
                url,
                timeout=REQUEST_TIMEOUT,
                **kwargs,
            )
        except httpx.TimeoutException as exc:
            if attempt < MAX_RETRIES - 1:
                delay = BASE_DELAY * (2**attempt) + random.uniform(0, 0.5)  # noqa: S311
                await asyncio.sleep(delay)
                continue
            msg = f"Request timed out after {MAX_RETRIES} attempts"
            raise ExportAPIError(platform, 0, msg) from exc

        if response.status_code < 400:
            return response

        if response.status_code in _NO_RETRY_STATUSES:
            if response.status_code in {401, 403}:
                raise ExportAPIAuthError(platform)
            raise ExportAPIError(platform, response.status_code, response.text[:200])

        if response.status_code in _RETRYABLE_STATUSES:
            if response.status_code == 429 and attempt == MAX_RETRIES - 1:
                raise ExportAPIRateLimitError(platform)
            if attempt < MAX_RETRIES - 1:
                delay = BASE_DELAY * (2**attempt) + random.uniform(0, 0.5)  # noqa: S311
                await asyncio.sleep(delay)
                continue

        raise ExportAPIError(platform, response.status_code, response.text[:200])

    # Should not reach here, but satisfy type checker
    msg = f"Request failed after {MAX_RETRIES} attempts"
    raise ExportAPIError(platform, 0, msg)
