"""Top-level shared fixtures for the entire test suite.

Every conftest under `tests/*/` was previously a near-copy of this
file — `tests/use_cases/conftest.py`, `tests/perf/conftest.py`,
`tests/stress/conftest.py`, `tests/conformance/conftest.py` all
spawned the broker the same way. The 2026-05-25 reliability pass
collapsed them into this single source.

What lives here:

* `broker_sock_path` — session-scoped path discriminated by pytest
  worker (see `_broker.default_sock_path`). Different suites can
  override the *suite* tag without giving up worker discrimination.
* `broker` — function-scoped Client connected to a freshly-spawned
  broker. Fails (not skips) when the binary is stale, with the
  exact rebuild command in the failure message.
* `sock_path` — passthrough of `broker_sock_path` for tests that
  open extra connections.

What does NOT live here:

* Suite-specific knobs (heavier teardown deadlines, alternative
  socket paths). Those belong in `tests/<suite>/conftest.py` and
  override via the standard pytest fixture mechanism.

**Federation reuse note.** Drop this file + `tests/_broker.py`
into any project that ships a binary-backed daemon and you get
the same skip/fail-fast behaviour for free.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest
from hypothesis import HealthCheck
from hypothesis import settings as _hyp_settings

from mgf.hrb import Client
from tests import _broker

if TYPE_CHECKING:
    from collections.abc import Iterator

# Hypothesis profile: disable the per-example wall-clock deadline. Our
# property tests are pure functions, but under `pytest -n auto` CPU
# contention an example can occasionally blow the default 200ms deadline
# and flake (observed on `test_duration_negative_clamps_to_zero`). The
# deadline guards against accidental slowness, which `max_examples` +
# pytest-timeout already cover here; drop it to keep parallel runs
# deterministic.
_hyp_settings.register_profile(
    "mgf-hrb",
    deadline=None,
    suppress_health_check=[HealthCheck.too_slow],
)
_hyp_settings.load_profile("mgf-hrb")


@pytest.fixture(scope="session")
def broker_sock_path() -> str:
    """Pytest-worker-discriminated Unix socket path. Single-worker
    runs use `/tmp/mgf-hrb-main.sock`; `pytest -n auto` workers
    use `…-gw0.sock`, `…-gw1.sock`, etc. (no collision).

    Session-scoped so every broker spawn in a given worker reuses
    the same path — pre-unlink on spawn handles leftover state.
    """
    return _broker.default_sock_path()


@pytest.fixture
def sock_path(broker_sock_path: str) -> str:
    """Alias kept for the multi-client tests that opened extra
    connections under the older suite-local conftests."""
    return broker_sock_path


@pytest.fixture
def broker(broker_sock_path: str) -> Iterator[Client]:
    """Yield a connected `Client` to a freshly-spawned broker.

    Skip-vs-fail policy:
      * Binary absent  → `pytest.skip` with the rebuild command
        (contributor hasn't built; not an error).
      * Binary STALE   → `pytest.fail` with the rebuild command
        (silent miss is the trap from the 2026-05-25 session; we
        make it loud).
      * Spawn timeout  → `pytest.fail` with the socket path.
    """
    if not _broker.server_exists():
        pytest.skip(
            f"Broker binary not built at {_broker.server_path()}. Run: {_broker.build_command()}"
        )
    is_stale, reason = _broker.broker_is_stale()
    if is_stale:
        pytest.fail(reason, pytrace=False)

    try:
        proc = _broker.spawn_broker(broker_sock_path)
    except TimeoutError as e:
        pytest.fail(str(e), pytrace=False)

    try:
        client = Client.connect(broker_sock_path)
        try:
            yield client
        finally:
            client.close()
    finally:
        _broker.teardown_broker(proc, broker_sock_path)
