from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import logging

logger = logging.getLogger(__name__)


def with_retry(
    max_attempts: int = 3,
    min_wait: float = 0.1,
    max_wait: float = 2.0,
    exceptions: tuple = (Exception,)
):
    """
    Decorator to retry a function call with exponential backoff.

    Args:
        max_attempts: Maximum number of attempts before giving up.
        min_wait: Minimum wait time between retries in seconds.
        max_wait: Maximum wait time between retries in seconds.
        exceptions: Tuple of exception types to retry on.
    """
    return retry(
        stop=stop_after_attempt(max_attempts),
        wait=wait_exponential(multiplier=min_wait, max=max_wait),
        retry=retry_if_exception_type(exceptions),
        before_sleep=lambda retry_state: logger.warning(
            f"Retrying {retry_state.fn.__name__} due to {retry_state.outcome.exception()} "
            f"(attempt {retry_state.attempt_number}/{max_attempts})"
        )
    )
