"""Contract: the `capabilities` op wire shape.

Pins every field a broker emits in a backend dict. The
`doctor` CLI reads these directly; the typed `BackendCapability`
projection reads them too. A field rename / removal here cascades
across every consumer.

This is the same set of assertions the older
`test_capabilities_returns_wire_payload` test in `test_client.py`
makes — promoted here for hygiene (graduated from "mixed with
unit tests" to "lives in the contract dir").
"""

from __future__ import annotations

from typing import Any

import pytest

from mgf.hrb import BackendCapability
from mgf.hrb._client import Client
from mgf.hrb.exceptions import from_wire

pytestmark = pytest.mark.contract


class _FakeTransport:
    """Same shape as the one in test_client.py — duplicated here so
    contract tests are independent of unit-test internals."""

    def __init__(self, responses: list[dict[str, Any]]) -> None:
        self.responses = list(responses)
        self.requests: list[dict[str, Any]] = []
        self.closed = False

    def call(self, **req: Any) -> dict[str, Any]:
        self.requests.append(req)
        resp = self.responses.pop(0)
        if not resp.get("ok"):
            raise from_wire(resp.get("err", "Unknown"))
        return resp

    def close(self) -> None:
        self.closed = True


def _make_client(responses: list[dict[str, Any]]) -> tuple[Client, _FakeTransport]:
    t = _FakeTransport(responses)
    return Client(t, cap_id=1, client_id=1), t  # type: ignore[arg-type]


# The canonical full-shape response a current broker emits for two
# workers. Used as the contract fixture — every field below is the
# wire's name; a rename breaks tests at the boundary.
_FULL_BACKENDS_WIRE = [
    {
        "scope_prefix": "hw:/pci/",
        "backend_name": "mgf-hrb-linux",
        "backend_version": "0.1.0",
        "resource_count": 36,
        "mode": "full",
        "mode_reason": "",
        "ops": {
            "enumerate": True,
            "query_topology": True,
            "watch": False,
            "acquire": True,
            "release": True,
            "reset": True,
            "map_region": True,
            "raw_channel": True,
            "enforce_cap": False,
        },
    },
    {
        "scope_prefix": "hw:/usb/",
        "backend_name": "mgf-hrb-usb",
        "backend_version": "0.1.0",
        "resource_count": 15,
        "mode": "full",
        "mode_reason": "",
        "ops": {
            "enumerate": True,
            "query_topology": True,
            "watch": True,
            "acquire": True,
            "release": True,
            "reset": True,
            "map_region": False,
            "raw_channel": True,
            "enforce_cap": False,
        },
    },
]


def test_capabilities_wire_has_six_canonical_fields_per_backend() -> None:
    """A backend dict MUST carry `scope_prefix`, `backend_name`,
    `backend_version`, `resource_count`, `mode`, `mode_reason`,
    `ops`. Any rename here breaks the doctor's per-worker grid
    rendering."""
    client, _ = _make_client([{"ok": True, "backends": _FULL_BACKENDS_WIRE}])
    raw = client.capabilities_raw()
    assert len(raw) == 2
    required = {
        "scope_prefix",
        "backend_name",
        "backend_version",
        "resource_count",
        "mode",
        "mode_reason",
        "ops",
    }
    for b in raw:
        missing = required - b.keys()
        assert not missing, f"backend dict missing fields: {missing}"


def test_capabilities_ops_grid_carries_nine_canonical_ops() -> None:
    """The `ops` dict inside a backend carries one boolean per
    seam op. The set of ops is the kabi vtable surface — adding an
    op is additive; renaming or removing one is breaking."""
    client, _ = _make_client([{"ok": True, "backends": _FULL_BACKENDS_WIRE}])
    raw = client.capabilities_raw()
    canonical_ops = {
        "enumerate",
        "query_topology",
        "watch",
        "acquire",
        "release",
        "reset",
        "map_region",
        "raw_channel",
        "enforce_cap",
    }
    for b in raw:
        assert canonical_ops.issubset(b["ops"].keys()), (
            f"backend {b['scope_prefix']} missing ops; have {set(b['ops'].keys())}"
        )


def test_capabilities_typed_projection_preserves_all_fields() -> None:
    """The SDK-05 typed projection over `capabilities()` MUST
    surface every field — a future field added to `from_wire`
    without a `BackendCapability` slot would silently drop on the
    way through. Pin so the projection stays a true window."""
    client, _ = _make_client([{"ok": True, "backends": _FULL_BACKENDS_WIRE}])
    typed = client.capabilities()
    assert len(typed) == 2
    for b in typed:
        assert isinstance(b, BackendCapability)
        assert b.scope_prefix
        assert b.backend_name
        assert b.backend_version  # forward-compat optional but populated here
        assert b.resource_count > 0
        assert b.mode  # ditto
        assert b.ops  # tuple, non-empty


def test_capabilities_degraded_mode_preserves_reason() -> None:
    """When a backend reports `mode != "full"`, the wire carries
    `mode_reason` (the operator-visible explanation). Pin so
    doctor can render the diagnostic rather than just OK/WARN."""
    degraded_wire = [
        {
            "scope_prefix": "hw:/pci/",
            "backend_name": "mgf-hrb-linux",
            "backend_version": "0.1.0",
            "resource_count": 36,
            "mode": "read_only",
            "mode_reason": "no /dev/vfio — acquire returns Unsupported",
            "ops": {
                "enumerate": True,
                "query_topology": True,
                "watch": False,
                "acquire": False,
                "release": True,
                "reset": False,
                "map_region": False,
                "raw_channel": False,
                "enforce_cap": False,
            },
        }
    ]
    client, _ = _make_client([{"ok": True, "backends": degraded_wire}])
    typed = client.capabilities()
    assert typed[0].mode == "read_only"
    assert typed[0].mode_reason is not None
    assert "no /dev/vfio" in typed[0].mode_reason
