import pytest

from mnemosynecore.retries import retry, retry_call


def test_retry_call_succeeds_after_retries():
    state = {"n": 0}
    sleeps = []
    retries = []

    def func():
        state["n"] += 1
        if state["n"] < 3:
            raise ValueError("fail")
        return "ok"

    out = retry_call(
        func,
        attempts=4,
        delay_sec=0.1,
        backoff=2.0,
        sleep_func=lambda sec: sleeps.append(sec),
        on_retry=lambda attempt, exc, sec: retries.append((attempt, str(exc), sec)),
    )

    assert out == "ok"
    assert state["n"] == 3
    assert sleeps == [0.1, 0.2]
    assert retries[0][0] == 1 and retries[1][0] == 2


def test_retry_call_raises_last_exception():
    state = {"n": 0}

    def func():
        state["n"] += 1
        raise RuntimeError("boom")

    with pytest.raises(RuntimeError, match="boom"):
        retry_call(func, attempts=3, sleep_func=lambda sec: None)
    assert state["n"] == 3


def test_retry_call_validates_arguments():
    with pytest.raises(ValueError):
        retry_call(lambda: 1, attempts=0)
    with pytest.raises(ValueError):
        retry_call(lambda: 1, delay_sec=-1)
    with pytest.raises(ValueError):
        retry_call(lambda: 1, backoff=0.5)
    with pytest.raises(ValueError):
        retry_call(lambda: 1, exceptions=[])


def test_retry_decorator():
    state = {"n": 0}

    @retry(attempts=2, delay_sec=0, sleep_func=lambda sec: None)
    def f():
        state["n"] += 1
        if state["n"] == 1:
            raise ValueError("once")
        return "done"

    assert f() == "done"
    assert state["n"] == 2

