"""
Utility functions for retry logic and progress tracking.
"""
import time
from typing import Callable, Optional
from .exceptions import NetworkError, TimeoutError, RateLimitError


def retry_with_backoff(
    func: Callable,
    max_retries: int = 3,
    initial_delay: float = 1.0,
    max_delay: float = 60.0,
    backoff_factor: float = 2.0,
    retry_on: tuple = (NetworkError, TimeoutError)
):
    """
    Retry a function with exponential backoff.
    
    Args:
        func: Function to retry
        max_retries: Maximum number of retry attempts
        initial_delay: Initial delay in seconds
        max_delay: Maximum delay in seconds
        backoff_factor: Multiplier for delay after each retry
        retry_on: Tuple of exception types to retry on
    
    Returns:
        Result of the function call
    
    Raises:
        The last exception if all retries fail
    """
    delay = initial_delay
    last_exception = None
    
    for attempt in range(max_retries + 1):
        try:
            return func()
        except retry_on as e:
            last_exception = e
            if attempt == max_retries:
                raise
            
            # Handle rate limit with retry-after header
            if isinstance(e, RateLimitError) and e.retry_after:
                time.sleep(e.retry_after)
            else:
                time.sleep(min(delay, max_delay))
                delay *= backoff_factor
    
    raise last_exception


class ProgressTracker:
    """Track upload/download progress."""
    
    def __init__(self, callback: Optional[Callable[[int, int], None]] = None):
        self.callback = callback
        self.total = 0
        self.current = 0
    
    def update(self, current: int, total: int):
        """Update progress."""
        self.current = current
        self.total = total
        if self.callback:
            self.callback(current, total)
    
    def __call__(self, current: int, total: int):
        """Allow using as a callable."""
        self.update(current, total)


class ProgressFileWrapper:
    """Wrapper for file objects to track upload progress."""
    
    def __init__(self, file_obj, callback: Callable[[int, int], None], total_size: int = -1):
        self.file_obj = file_obj
        self.callback = callback
        self.total_size = total_size
        self.bytes_read = 0
    
    def read(self, size=-1):
        """Read from file and update progress."""
        data = self.file_obj.read(size)
        self.bytes_read += len(data)
        if self.callback and self.total_size > 0:
            self.callback(self.bytes_read, self.total_size)
        return data
    
    def seek(self, *args, **kwargs):
        """Seek in file."""
        result = self.file_obj.seek(*args, **kwargs)
        self.bytes_read = self.file_obj.tell()
        return result
    
    def tell(self):
        """Get current position."""
        return self.file_obj.tell()
    
    def __getattr__(self, name):
        """Delegate other attributes to wrapped file."""
        return getattr(self.file_obj, name)
