"""Retry logic with exponential backoff."""

import asyncio
from collections.abc import Callable
from functools import wraps
from typing import Any, TypeVar

from growflow_billing.exceptions import RateLimitError, ServerError

T = TypeVar("T")


async def with_retry(
    func: Callable[..., T],
    max_retries: int = 3,
    retry_delay: float = 1.0,
    *args: Any,
    **kwargs: Any,
) -> T:
    """Execute function with retry logic.

    Retries on:
    - RateLimitError (429)
    - ServerError (5xx)
    - Connection errors

    Uses exponential backoff: delay * (2 ** attempt)

    Args:
        func: Async function to execute
        max_retries: Maximum retry attempts
        retry_delay: Base delay between retries (seconds)
        *args: Function arguments
        **kwargs: Function keyword arguments

    Returns:
        Function result

    Raises:
        Last exception if all retries fail
    """
    last_exception: Exception | None = None

    for attempt in range(max_retries + 1):
        try:
            return await func(*args, **kwargs)
        except (RateLimitError, ServerError) as e:
            last_exception = e
            if attempt < max_retries:
                delay = retry_delay * (2**attempt)
                await asyncio.sleep(delay)
        except Exception as e:
            # Don't retry other errors
            raise e

    if last_exception:
        raise last_exception
    raise RuntimeError("Unexpected retry state")


def retryable(
    max_retries: int = 3,
    retry_delay: float = 1.0,
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """Decorator to make a function retryable.

    Example:
        @retryable(max_retries=3)
        async def fetch_data():
            ...
    """

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @wraps(func)
        async def wrapper(*args: Any, **kwargs: Any) -> T:
            return await with_retry(func, max_retries, retry_delay, *args, **kwargs)

        return wrapper

    return decorator
