"""WebSocket reconnection logic with exponential backoff."""

from __future__ import annotations

import asyncio
from typing import Awaitable, Callable

from .types import ReconnectConfig


class ReconnectManager:
    """Manages reconnection attempts with exponential backoff."""

    def __init__(self, config: ReconnectConfig | None = None) -> None:
        self._config = config or ReconnectConfig()
        self._attempts = 0
        self._task: asyncio.Task[None] | None = None
        self._cancelled = False

    async def schedule_reconnect(
        self,
        on_reconnect: Callable[[], Awaitable[None]],
    ) -> None:
        """Schedule a reconnection attempt.

        Args:
            on_reconnect: Async callback to execute when it's time to reconnect

        Raises:
            Exception: If max attempts reached or reconnection fails
        """
        # Check if max attempts reached
        if (
            self._config.max_attempts is not None
            and self._attempts >= self._config.max_attempts
        ):
            raise Exception("Maximum reconnection attempts reached")

        # Calculate delay with exponential backoff
        delay = min(
            self._config.initial_delay * (self._config.multiplier**self._attempts),
            self._config.max_delay,
        )

        self._attempts += 1
        self._cancelled = False

        # Wait for delay
        await asyncio.sleep(delay)

        # Check if cancelled during sleep
        if self._cancelled:
            return

        # Execute reconnection
        await on_reconnect()

    def reset(self) -> None:
        """Reset the reconnection state (call after successful connection)."""
        self._attempts = 0
        self.cancel()

    def cancel(self) -> None:
        """Cancel any pending reconnection attempt."""
        self._cancelled = True
        if self._task and not self._task.done():
            self._task.cancel()
            self._task = None

    @property
    def current_attempt(self) -> int:
        """Get the current attempt number."""
        return self._attempts

    @property
    def next_delay(self) -> float:
        """Get the delay for the next reconnection attempt (in seconds)."""
        return min(
            self._config.initial_delay * (self._config.multiplier**self._attempts),
            self._config.max_delay,
        )

    @property
    def is_max_attempts_reached(self) -> bool:
        """Check if max attempts have been reached."""
        if self._config.max_attempts is None:
            return False
        return self._attempts >= self._config.max_attempts

    def destroy(self) -> None:
        """Destroy the reconnect manager and cancel any pending reconnection."""
        self.cancel()
        self._attempts = 0
