"""Conformance: G2 — the model never lies, especially about emptiness.

G2 (design Part 1, "Accuracy") promises: when the broker doesn't
know something, it reports `unknown` / `[]` / `null` honestly rather
than guessing. The cross-wire tests here pin that property at the
SDK seam — a future regression that returns a fabricated default
would slip past unit tests but fail here.

Pinned scenarios:

  * `list(scope_no_match) -> []` (not "no such scope" exception)
  * `inspect(unknown_urn) -> NotFoundError` (typed, not silent
    empty Resource)
  * `drain_events(quiet_bus) -> []` (not blocking, not made-up
    events)
  * `list_leases(no_holders) -> []`
  * `capabilities()` reports `mode != "full"` honestly when
    substrate is missing
"""

# ruff: noqa: N802 — conformance test names cite design promises (G2/G3/S2/etc.); keeping the spec's case improves traceability.

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from mgf.hrb import NotFoundError

if TYPE_CHECKING:
    from mgf.hrb import Client

pytestmark = [pytest.mark.conformance, pytest.mark.timeout(60)]


def test_G2_list_on_empty_scope_returns_empty_list(broker: Client) -> None:
    """G2 honesty: an empty scope query returns an empty list, NOT
    a typed error nor fabricated data. Consumers iterate `for urn
    in client.list(scope)` and expect zero iterations on miss."""
    devs = broker.list("hw:/nonexistent_subtree/**")
    assert devs == [], f"empty-scope list returned {devs!r} — fabricated data?"


def test_G2_inspect_unknown_urn_raises_typed_not_found(broker: Client) -> None:
    """G2 honesty: inspect on a URN no worker owns raises
    NotFoundError. A silent empty Resource would let a consumer
    proceed as if the device existed — exactly the lie G2 forbids."""
    with pytest.raises(NotFoundError):
        broker.inspect("hw:/not_a_real_bus/dev_that_doesnt_exist")


def test_G2_drain_events_on_quiet_bus_returns_empty_list(broker: Client) -> None:
    """G2 honesty: no events queued → empty list. Not blocking
    waiting for events to materialize; not returning a fake "all
    good" sentinel."""
    events = broker.drain_events("hw:/test/**")
    assert events == [], f"drain_events on quiet bus returned {events!r} — fake event?"


def test_G2_list_leases_when_no_one_holds_returns_empty(broker: Client) -> None:
    """G2 honesty: zero live leases → `[]`. The hrb-app lease pane
    counts on this for its "currently held by: none" display."""
    leases = broker.list_leases("hw:/test/**")
    assert leases == []


def test_G2_capabilities_reports_honest_mode_per_worker(broker: Client) -> None:
    """G2 honesty: every backend reports its `mode` truthfully —
    `full` only when the substrate is genuinely present. On a host
    without /dev/vfio, the PCI worker reports `degraded_no_iommu`
    (not `full`); the test environment has the fake worker which
    is always `full`. Pin: mode is a documented value, never empty."""
    caps = broker.capabilities()
    documented_modes = {
        "full",
        "read_only",
        "degraded_no_iommu",
        "degraded_other",
    }
    for c in caps:
        assert c.mode is not None, f"backend {c.scope_prefix} has no mode — G2 violation"
        assert c.mode in documented_modes, (
            f"backend {c.scope_prefix} reports unknown mode {c.mode!r}"
        )


def test_G2_inspect_driver_attrs_present_when_bound_absent_when_not(
    broker: Client,
) -> None:
    """G2 honesty for the new driver-ownership attrs (Part 13 §5.2.1):
    `driver` + `module` MUST appear when a kernel driver is bound to
    the device, and MUST be honestly absent (the attrs simply aren't
    emitted; the key is missing from the tuple) when the device has
    no bound driver.

    The fake backend (`hw:/test/`) has no real driver — its inspect
    response MUST NOT carry these attrs. Lying about a bound driver
    would teach a consumer the broker carries information it can't
    verify.
    """
    fake_detail = broker.inspect("hw:/test/dev0")
    fake_attrs = dict(fake_detail.attrs)
    assert "driver" not in fake_attrs, (
        f"fake backend's inspect emitted a driver attr "
        f"({fake_attrs['driver']!r}) — G2 violation, the fake worker "
        "has no real driver to report"
    )
    assert "module" not in fake_attrs, (
        f"fake backend's inspect emitted a module attr ({fake_attrs['module']!r}) — G2 violation"
    )

    # USB: at least one interface on this host MUST be bound (the
    # host's keyboards / cameras / hubs are virtually always bound at
    # the kernel level). If NONE are bound, the test environment is
    # too pathological to validate from; skip honestly.
    usb_urns = broker.list("hw:/usb/**")
    if not usb_urns:
        pytest.skip("no USB interfaces enumerated; cannot test the bound path")

    seen_with_driver = False
    for urn in usb_urns[:20]:
        detail = broker.inspect(urn)
        drv = detail.attr("driver")
        mod = detail.attr("module")
        if drv is not None:
            seen_with_driver = True
            # When `driver` is present, `module` SHOULD also be present
            # (a bound driver almost always comes from a loaded module).
            # The exception is drivers built into the kernel; we don't
            # assert mod-is-not-None defensively for that reason.
            assert isinstance(drv, str) and len(drv) > 0, (
                f"inspect({urn}) returned an empty driver attr"
            )
            if mod is not None:
                assert isinstance(mod, str) and len(mod) > 0, (
                    f"inspect({urn}) returned an empty module attr"
                )
            break
    assert seen_with_driver, (
        f"none of the first 20 USB interfaces ({len(usb_urns)} total) "
        "reported a bound driver — host environment too pathological "
        "to validate the bound-path G2 assertion"
    )


def test_G2_doctor_deferred_means_we_dont_know_yet() -> None:
    """G2 honesty: a check the engine can't run yet reports
    DEFERRED — never OK, never WARN.

    R-USB-PKG (2026-05-27) reshaped the pin. Pre-R-USB-PKG the
    canonical DEFERRED example was `UDEV_RULE_INSTALLED` (waiting
    on the udev rule template to ship). The template shipped; that
    check is now active (OK / WARN). The principle still holds —
    expressed here via `BACKEND_CAPABILITIES` in the no-client
    branch (when `run_all` is called without a live `Client`, the
    capabilities query short-circuits to DEFERRED rather than
    pretending to have an answer).
    """
    from mgf.hrb import DoctorSeverity, diagnostics

    # No client → BACKEND_CAPABILITIES MUST be DEFERRED (it needs a
    # live broker to know the truth). The local-only checks (sysfs,
    # IOMMU, etc.) still run; only the broker-side one is DEFERRED.
    findings = diagnostics.run_all("/nonexistent/socket.sock", client=None)
    backend_check = next(f for f in findings if f.code == "BACKEND_CAPABILITIES")
    assert backend_check.severity is DoctorSeverity.DEFERRED, (
        f"BACKEND_CAPABILITIES reported {backend_check.severity} with no "
        "client — G2 violation (must be DEFERRED until we can actually ask "
        "the broker)"
    )
