"""
Unit test: ExponentialBackoff
"""

import asyncio
import time

import pytest

from app.imap.reconnect import ExponentialBackoff


@pytest.mark.asyncio
async def test_backoff_increases():
    """Il tempo di attesa aumenta ad ogni tentativo."""
    backoff = ExponentialBackoff(label="test", jitter=False)

    # Registra i tempi senza aspettare davvero (patch asyncio.sleep)
    waits = []

    original_sleep = asyncio.sleep

    async def fake_sleep(t):
        waits.append(t)

    asyncio.sleep = fake_sleep
    try:
        await backoff.wait()
        await backoff.wait()
        await backoff.wait()
    finally:
        asyncio.sleep = original_sleep

    assert waits[1] > waits[0], "Il secondo wait deve essere maggiore del primo"
    assert waits[2] > waits[1], "Il terzo wait deve essere maggiore del secondo"


@pytest.mark.asyncio
async def test_backoff_reset():
    """reset() riporta il contatore a zero e il wait riparte dal valore iniziale."""
    backoff = ExponentialBackoff(label="test", jitter=False)
    waits = []

    async def fake_sleep(t):
        waits.append(t)

    original_sleep = asyncio.sleep
    asyncio.sleep = fake_sleep
    try:
        await backoff.wait()   # waits[0]: valore iniziale (es. 1.0)
        await backoff.wait()   # waits[1]: valore incrementato (es. 2.0)
        backoff.reset()
        await backoff.wait()   # waits[2]: deve tornare al valore iniziale
    finally:
        asyncio.sleep = original_sleep

    # Dopo reset il wait riparte dal valore iniziale
    assert backoff.attempt == 1
    assert waits[2] == waits[0], (
        f"Dopo reset il wait deve tornare a {waits[0]}, ma era {waits[2]}"
    )


@pytest.mark.asyncio
async def test_backoff_max():
    """Il tempo di attesa non supera backoff_max_seconds."""
    from app.config import get_settings
    settings = get_settings()

    backoff = ExponentialBackoff(label="test", jitter=False)
    max_recorded = 0.0

    async def fake_sleep(t):
        nonlocal max_recorded
        max_recorded = max(max_recorded, t)

    original_sleep = asyncio.sleep
    asyncio.sleep = fake_sleep
    try:
        for _ in range(20):
            await backoff.wait()
    finally:
        asyncio.sleep = original_sleep

    assert max_recorded <= settings.backoff_max_seconds + 0.1


def test_backoff_attempt_count():
    """Il contatore tentativi si incrementa correttamente."""
    import asyncio
    backoff = ExponentialBackoff(label="test", jitter=False)

    async def run():
        async def fake_sleep(_):
            pass

        asyncio.sleep = fake_sleep
        await backoff.wait()
        await backoff.wait()
        await backoff.wait()

    asyncio.get_event_loop().run_until_complete(run())
    assert backoff.attempt == 3
