import asyncio
import functools
from typing import Callable, Any, Union, Tuple, Type
from loguru import logger


def base_retry(
    func=None,
    *,
    name: str = None,
    delays_sec: Union[list, tuple, float, int] = 1,
    exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = Exception,
    ignore_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = None
) -> Callable:
    """
    异步重试装饰器，专门用于装饰异步函数
    支持自定义重试次数（由 delays 列表长度决定）、时间间隔和异常类型
    """

    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def wrapper(*args: Any, **kwargs: Any) -> Any:
            # 处理 delays 参数
            if isinstance(delays_sec, (int, float)):
                # 如果是单个数值，创建包含该值的列表
                delay_list = [delays_sec]
            else:
                # 如果是列表或元组，直接使用
                delay_list = list(delays_sec)

            func_name = name or func.__name__

            idx = 0
            while True:
                try:
                    return await func(*args, **kwargs)
                except exceptions as e:
                    if ignore_exceptions and isinstance(e, ignore_exceptions):
                        logger.info(
                            f"Ignoring exception [{type(e).__name__}] in [{func_name}]"
                        )
                        raise

                    last_exception = e

                    if idx < len(delay_list):
                        delay = delay_list[idx]
                        logger.warning(
                            f"Attempt [{func_name}] {idx + 1} failed. Retrying in {delay} seconds..."
                        )
                        await asyncio.sleep(delay)
                        idx += 1
                    else:
                        logger.error(
                            f"Attempt [{func_name}] {idx + 1} total. No more retries."
                        )
                        break

            raise last_exception

        return wrapper

    return decorator if func is None else decorator(func)



