"""Retry helpers for transient Redis startup errors."""

from __future__ import annotations

import asyncio
from collections.abc import Awaitable, Callable
from typing import TypeVar

from app.core.logger import logger

T = TypeVar("T")


def is_redis_loading_error(exc: BaseException) -> bool:
    message = str(exc).lower()
    return (
        "loading redis" in message
        or "loading the dataset" in message
        or "busyloading" in message
    )


async def retry_while_redis_loading(
    operation: str,
    fn: Callable[[], Awaitable[T]],
    *,
    max_backoff_seconds: float = 30.0,
) -> T:
    backoff = 0.5
    while True:
        try:
            return await fn()
        except BaseException as exc:
            if not is_redis_loading_error(exc):
                raise
            logger.warning(
                "Redis loading, retrying stream setup operation={operation} backoff_s={backoff} error={error}",
                operation=operation,
                backoff=backoff,
                error=str(exc),
            )
            await asyncio.sleep(backoff)
            backoff = min(backoff * 2, max_backoff_seconds)
