"""Minimal test: check-then-set lazy-loading under simulated multi-worker conditions.

This test uses multiprocessing.Process (separate interpreter per worker, like
uvicorn --workers N) to exercise the documented unsafe check-then-set path in
accordance_api. Under separate processes the race is harmless (independent
_module dicts), but the test verifies no crashes, deadlocks, or data
corruption when multiple processes concurrently import and lazy-load modules.
"""

from __future__ import annotations
import multiprocessing
import queue


def _worker_lazy_load(abbr: str | None, results_queue: multiprocessing.Queue) -> None:
    """Child process: import accordance_api, lazy-load a text module, read a verse.

    Each process gets its own interpreter with independent module-level state,
    simulating a separate uvicorn worker.
    """
    try:
        if abbr is None:
            results_queue.put(("SKIP", "no text modules discovered"))
            return

        import accordance_api

        mod = accordance_api._get_text_module(abbr)
        text = mod.get_verse("Genesis 1:1")
        if not text or not text.strip():
            results_queue.put(("FAIL", f"Empty Genesis 1:1 text for {abbr}"))
            return

        results_queue.put(("OK", text.strip()))
    except Exception as exc:
        results_queue.put(("FAIL", f"{type(exc).__name__}: {exc}"))


def _discover_first_text_abbr() -> str | None:
    """Return the first available Bible module abbreviation, or None."""
    try:
        from accordance_parser.discovery import discover_modules
    except Exception:
        return None
    mods = discover_modules()
    for abbr, info in mods.items():
        if info.get("type") == 1:
            return abbr
    return None


def test_multiprocess_lazy_load_no_crash() -> None:
    """Spawn N separate processes that each lazy-load a module concurrently.

    Verifies the check-then-set path in _get_text_module does not crash or
    corrupt data when multiple interpreters load the same module simultaneously.
    """
    abbr = _discover_first_text_abbr()
    if abbr is None:
        return  # skip — no text modules available (not a failure)

    n_workers = 4
    # "spawn" is the safest start method and closest to uvicorn behavior
    ctx = multiprocessing.get_context("spawn")
    results_queue: multiprocessing.Queue = ctx.Queue()

    processes = []
    for _ in range(n_workers):
        p = ctx.Process(target=_worker_lazy_load, args=(abbr, results_queue))
        processes.append(p)
        p.start()

    # Collect results with timeout to catch hangs
    results: list[tuple[str, str]] = []
    deadline = 60  # seconds
    for p in processes:
        p.join(timeout=deadline)
        if p.is_alive():
            p.terminate()
            p.join()
            results.append(("FAIL", "process timed out"))

    # Drain queue (non-blocking — all processes are done by now)
    while True:
        try:
            results.append(results_queue.get(timeout=0.5))
        except queue.Empty:
            break

    # Assertions
    assert len(results) == n_workers, (
        f"Expected {n_workers} results, got {len(results)}: {results}"
    )

    oks = [r for r in results if r[0] == "OK"]
    assert len(oks) >= 1, f"No workers succeeded: {results}"

    # All successful workers must return the same Genesis 1:1 text
    first_text = oks[0][1]
    for status, text in oks:
        assert text == first_text, (
            f"Mismatched verse text across workers: {text[:80]!r} vs {first_text[:80]!r}"
        )

    failures = [r for r in results if r[0] == "FAIL"]
    assert len(failures) == 0, f"Workers failed: {failures}"
