import asyncio
from functools import wraps
from .errors.errs import BadResponse, AttemptLimit, ServerError
from random import uniform
from httpx import ConnectTimeout, ConnectError




def retry(coro):
    """
    Экспоненциальный backoff для асинхронных вызовов, возвращающих httpx.Response.

    Поведение:
    - Возврат сразу при .is_success == True.
    - Если статус в allowed_statuses → поднимаем ServerError → ретраим.
    - Любой сетевой сбой (timeout/connection/protocol) → ретраим.
    - Любой другой неуспех (обычно 4xx/5xx, кроме allowed_statuses) → BadResponse и без ретраев.

    """
    @wraps(coro)
    async def inner(*args, **kwargs):
        CORO_NAME = f"{coro.__qualname__}"
        LIMIT = 3
        ALLOWED_STATUSES = {429, 503}

        for attempt in range(1, LIMIT+1):
            try:
                result = await coro(*args, **kwargs)
                status_code = getattr(result, "status_code", None)
                if not status_code:
                    raise AttributeError(
                        f"AttributeError occurred status_code is {status_code} {CORO_NAME}"
                        )
                if result.is_success:
                    return result
                if status_code in ALLOWED_STATUSES:
                    raise ServerError(
                        f"ServerError occurred status_code {status_code} {CORO_NAME}"
                        )
                else:
                    raise BadResponse(
                        f"BadResponse occurred status_code"
                        , status_code)   
            except (asyncio.TimeoutError, ConnectTimeout, ConnectError, ServerError) as e:
                delay = min(2 ** (attempt-1), 30) + uniform(0,1)
                await asyncio.sleep(delay)
        
        err = AttemptLimit(f"AttemptLimit occurred attempts {CORO_NAME}", LIMIT)
        raise err

    return inner




