# -----------------------------------------------------------
# Astra - WhatsApp Userbot Framework
# Licensed under the MIT License.
# -----------------------------------------------------------

"""
This module provides a strategy for retrying failed connections.
It uses exponential backoff to avoid overwhelming the server.
"""

import random
import logging
from ..constants import MAX_RECONNECT_ATTEMPTS, RECONNECT_DELAY_MAX

logger = logging.getLogger("Astra.Connection")

class RetryStrategy:
    """
    A resilient retry manager for connection recovery.
    
    Implements exponential backoff with random jitter to ensure 
    reconnections happen in a nature-friendly way.
    """

    def __init__(
        self,
        base_delay: float = 2.0,
        max_delay: float = RECONNECT_DELAY_MAX,
        max_attempts: int = MAX_RECONNECT_ATTEMPTS
    ):
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.max_attempts = max_attempts
        self._current_attempt = 0

    def reset(self):
        """Resets the attempt counter (usually called on successful connection)."""
        if self._current_attempt > 0:
            logger.info("Connection stable. Resetting retry policy.")
        self._current_attempt = 0

    def next_delay(self) -> Optional[float]:
        """
        Calculates the wait time for the next reconnection attempt.
        
        Returns:
            The delay in seconds, or None if the retry limit is reached.
        """
        if self._current_attempt >= self.max_attempts:
            return None
        
        # Exponential growth: 2, 4, 8, 16...
        backoff = min(self.base_delay * (2 ** self._current_attempt), self.max_delay)
        
        # Add Jitter (0.7 to 1.3 variant) to prevent thundering herd
        delay = backoff * random.uniform(0.7, 1.3)
        
        self._current_attempt += 1
        logger.debug(f"Retrying connection (Attempt {self._current_attempt}/{self.max_attempts}) in {delay:.2f}s")
        
        return delay

    @property
    def current_attempt(self) -> int:
        return self._current_attempt
