"""JobRunner lifecycle: crash-restart, no duplicate purge task, stop() cancels
a pending restart (no task resurrection after shutdown)."""
import asyncio
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent.parent.parent / "src"))
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from app.jobs import JobRunner, MemoryJobStore


class _Eng:
    pass


def _runner():
    return JobRunner(_Eng(), MemoryJobStore(), ttl_s=3600)


def test_double_start_no_duplicate_tasks():
    async def scenario():
        r = _runner()
        r.start()
        task1, purge1 = r._task, r._purge_task
        r.start()  # idempotent
        assert r._task is task1 and r._purge_task is purge1
        await r.stop()

    asyncio.run(scenario())


def test_worker_restarts_but_keeps_single_purge():
    async def scenario():
        r = _runner()
        r.start()
        purge = r._purge_task
        # Simulate the worker crashing: cancel-free failure path via done-callback.
        # Force _run to have "exited with exception" by replacing it.
        r._task.cancel()
        try:
            await r._task
        except asyncio.CancelledError:
            pass
        # A cancelled task must NOT trigger a restart.
        await asyncio.sleep(0)
        assert r._restart_handle is None
        # The purge task is untouched by a worker cancel.
        assert r._purge_task is purge
        await r.stop()

    asyncio.run(scenario())


def test_stop_cancels_pending_restart():
    async def scenario():
        r = _runner()
        r.start()
        # Manually schedule a restart (as _on_run_done would on a crash).
        loop = asyncio.get_running_loop()
        r._restart_handle = loop.call_later(2.0, r._restart)
        await r.stop()
        # stop() must have cancelled the timer and left tasks torn down.
        assert r._restart_handle is None
        assert r._task is None and r._purge_task is None
        # Past the backoff window, nothing resurrected.
        await asyncio.sleep(0.05)
        assert r._task is None and r._purge_task is None

    asyncio.run(scenario())


def test_restart_noop_when_stopped():
    async def scenario():
        r = _runner()
        r.start()
        await r.stop()
        assert r._stopped is True
        r._restart()  # must not recreate tasks after stop
        assert r._task is None and r._purge_task is None

    asyncio.run(scenario())
