"""
Retry logic for API calls
"""

import time
from typing import Callable, TypeVar, Optional
from functools import wraps
import httpx

T = TypeVar('T')


class RetryConfig:
    """Retry configuration"""
    
    def __init__(
        self,
        max_retries: int = 3,
        backoff_factor: float = 1.0,
        retryable_status_codes: tuple = (429, 500, 502, 503, 504),
    ):
        self.max_retries = max_retries
        self.backoff_factor = backoff_factor
        self.retryable_status_codes = retryable_status_codes


def retry_on_failure(
    config: Optional[RetryConfig] = None,
    exceptions: tuple = (Exception,),
):
    """
    Decorator for retrying function calls on failure.
    
    Args:
        config: Retry configuration
        exceptions: Tuple of exceptions to catch and retry
    
    Returns:
        Decorated function with retry logic
    """
    if config is None:
        config = RetryConfig()
    
    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @wraps(func)
        def wrapper(*args, **kwargs) -> T:
            last_exception = None
            
            for attempt in range(config.max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    
                    # Check if we should retry
                    if attempt < config.max_retries:
                        # Check if it's a retryable HTTP error
                        status_code = None
                        if isinstance(e, httpx.HTTPStatusError):
                            status_code = e.response.status_code
                        elif hasattr(e, 'status_code'):
                            status_code = e.status_code
                        elif hasattr(e, 'response') and hasattr(e.response, 'status_code'):
                            status_code = e.response.status_code
                        
                        if status_code and status_code not in config.retryable_status_codes:
                            # Not retryable, raise immediately
                            raise
                        
                        # Calculate backoff delay
                        delay = config.backoff_factor * (2 ** attempt)
                        time.sleep(delay)
                    else:
                        # Max retries reached
                        raise
            
            # Should never reach here, but just in case
            if last_exception:
                raise last_exception
        
        return wrapper
    return decorator

