"""Tests for retry_with_backoff."""

from __future__ import annotations

import pytest

from main import retry_with_backoff


class TestRetryWithBackoff:
    """Tests for retry_with_backoff."""

    @pytest.mark.asyncio()
    async def test_success_first_attempt(self) -> None:
        """Successful call returns immediately."""
        call_count = 0

        async def succeed():
            nonlocal call_count
            call_count += 1
            return 42

        result = await retry_with_backoff(succeed)
        assert 42 == result
        assert 1 == call_count

    @pytest.mark.asyncio()
    async def test_retry_then_succeed(self) -> None:
        """Retries on failure then succeeds."""
        attempts = 0

        async def fail_then_succeed():
            nonlocal attempts
            attempts += 1
            if attempts < 3:
                msg = "not yet"
                raise RuntimeError(msg)
            return "ok"

        result = await retry_with_backoff(fail_then_succeed)
        assert "ok" == result
        assert 3 == attempts

    @pytest.mark.asyncio()
    async def test_all_retries_exhausted(self) -> None:
        """Raises last exception after max retries."""

        async def always_fail():
            msg = "fail"
            raise ValueError(msg)

        with pytest.raises(ValueError, match="fail"):
            await retry_with_backoff(always_fail, max_retries=2)

    @pytest.mark.asyncio()
    async def test_invalid_max_retries(self) -> None:
        """Zero max_retries raises ValueError."""

        async def noop():
            pass

        with pytest.raises(
            ValueError, match="max_retries must be at least 1"
        ):
            await retry_with_backoff(noop, max_retries=0)

    @pytest.mark.asyncio()
    async def test_single_retry(self) -> None:
        """max_retries=1 means only one attempt."""

        async def always_fail():
            msg = "fail"
            raise RuntimeError(msg)

        with pytest.raises(RuntimeError, match="fail"):
            await retry_with_backoff(always_fail, max_retries=1)
