from __future__ import annotations

import functools
import time
import threading
from typing import Callable, Any
import logging

logger = logging.getLogger(__name__)


def with_retry(
    max_retries: int = 3,
    backoff_base: float = 2.0,
    stop_event: threading.Event | None = None,
):
    """
    Decorator that retries the wrapped function upon exception.
    """

    def decorator(func: Callable[..., Any]) -> Callable[..., Any | None]:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            retries = 0
            while True:
                if stop_event and stop_event.is_set():
                    return None

                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if retries >= max_retries:
                        # Log error if needed or just re-raise
                        raise e

                    sleep_time = backoff_base**retries
                    if stop_event:
                        # Wait with event support
                        if stop_event.wait(sleep_time):
                            return None
                    else:
                        time.sleep(sleep_time)

                    retries += 1

        return wrapper

    return decorator
