from grantflow.core.job_runner import RedisJobRunner
from grantflow.tests.job_runner_helpers import (
    FakeRedisClient,
    clear_redis_observed,
    redis_client_factory,
    redis_observed_values,
    redis_test_task,
    wait_until,
)


def test_redis_job_runner_retries_then_succeeds():
    fake_client = FakeRedisClient()
    seen = {"calls": 0}

    def _flaky(value: int) -> None:
        seen["calls"] += 1
        if seen["calls"] == 1:
            raise RuntimeError("transient")
        redis_test_task(value)

    clear_redis_observed()
    runner = RedisJobRunner(
        worker_count=1,
        queue_maxsize=8,
        redis_url="redis://local-test/0",
        queue_name="grantflow:test:jobs:retry-success",
        pop_timeout_seconds=0.1,
        max_attempts=3,
        redis_client_factory=redis_client_factory(fake_client),
    )
    try:
        assert runner.submit(_flaky, 9) is True
        assert wait_until(lambda: redis_observed_values() == [9], timeout_s=2.0)
        diag = runner.diagnostics()
        assert diag["completed_count"] >= 1
        assert diag["failed_count"] == 0
        assert diag["retry_count"] >= 1
        assert diag["dead_lettered_count"] == 0
        assert fake_client.queue_snapshot(diag["dead_letter_queue_name"]) == []
    finally:
        runner.stop()


def test_redis_job_runner_dead_letters_after_max_attempts():
    fake_client = FakeRedisClient()

    def _always_fail(_value: int) -> None:
        raise RuntimeError("always bad")

    runner = RedisJobRunner(
        worker_count=1,
        queue_maxsize=8,
        redis_url="redis://local-test/0",
        queue_name="grantflow:test:jobs:retry-fail",
        pop_timeout_seconds=0.1,
        max_attempts=2,
        redis_client_factory=redis_client_factory(fake_client),
    )
    try:
        assert runner.submit(_always_fail, 5) is True
        assert wait_until(lambda: runner.diagnostics()["dead_lettered_count"] >= 1, timeout_s=2.0)
        diag = runner.diagnostics()
        assert diag["failed_count"] >= 1
        assert diag["retry_count"] >= 1
        assert diag["dead_lettered_count"] >= 1
        dlq_payloads = fake_client.queue_snapshot(diag["dead_letter_queue_name"])
        assert len(dlq_payloads) >= 1
    finally:
        runner.stop()
