import threading

from dashboard.jobs import JobRegistry


def test_job_registry_atomically_reserves_only_one_job_per_kind(tmp_path):
    registry = JobRegistry(tmp_path / "jobs.json")
    barrier = threading.Barrier(10)
    results = []

    def reserve():
        barrier.wait()
        results.append(registry.reserve("refresh", details={"full": False}))

    threads = [threading.Thread(target=reserve) for _ in range(10)]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()

    reserved = [job for job in results if job is not None]
    assert len(reserved) == 1
    assert reserved[0]["id"]
    assert reserved[0]["status"] == "queued"
    assert registry.active("refresh")["id"] == reserved[0]["id"]


def test_job_registry_tracks_completion_and_result(tmp_path):
    registry = JobRegistry(tmp_path / "jobs.json")
    job = registry.reserve("history")

    result = registry.run(job["id"], lambda: {"records": 42})

    assert result == {"records": 42}
    saved = registry.get(job["id"])
    assert saved["status"] == "complete"
    assert saved["result"] == {"records": 42}
    assert saved["started_at"]
    assert saved["finished_at"]
    assert registry.active("history") is None


def test_job_registry_marks_abandoned_active_jobs_interrupted_on_restart(tmp_path):
    path = tmp_path / "jobs.json"
    first = JobRegistry(path)
    job = first.reserve("refresh")

    restarted = JobRegistry(path)

    recovered = restarted.get(job["id"])
    assert recovered["status"] == "interrupted"
    assert recovered["finished_at"]
    assert restarted.active("refresh") is None
