"""Contract: the LEASE ENGINE's own events, on the wire (M-B chunk B1).

Before B1 the broker's core mutated the one fact nothing else owns —
who holds what, right now — and told nobody. ``pushFanout`` had a single
caller, the ``drain_events`` formatter, so an event reached a subscriber
only as a side effect of somebody polling, and leases had no event path
at all. B1 makes every committed mutation announce itself at the source.

Pinned here (live broker, two connections — the multi-operator shape the
milestone exists for):

  * **A claim by operator B reaches operator A's subscription** as
    ``{"kind":"ownership_changed","urn":...,"old":null,"new":<client>}``.
  * **A release** renders the mirror image: ``old:<client>, new:null``.
  * **A quarantine** renders ``{"kind":"faulted","urn":...,"code":<n>}``,
    where ``code`` is the ``QuarantineReason`` discriminant.
  * **Nothing renders as** ``{"kind":"other"}``. That catch-all was the
    pre-fold behaviour for these two kinds and it is a lie by omission:
    the event IS nameable, and a consumer told "other" cannot act on it.
    B3/B4 must never be tempted to parse it.

The equivalent byte-exact assertions run without a broker in
``hrb-code/src/format_test.zig``; these are the end-to-end truth.
"""

from __future__ import annotations

import json
import socket
from typing import Any

import pytest

from tests.contract.conftest import _SOCK_PATH, rt

pytestmark = pytest.mark.contract


# The synthetic worker the broker always carries for wire tests; claiming
# it touches no real hardware (server.zig seeds `hw:/test/dev0`).
_URN = "hw:/test/dev0"


def _second_connection() -> tuple[socket.socket, int]:
    """A SECOND operator on the same broker, with its own connection-bound
    cap. Subscriptions are keyed to the connection, so the subscriber and
    the mutator must be distinct — that is precisely the concurrency this
    milestone serves. Returns ``(socket, cap_id)``."""
    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    s.connect(_SOCK_PATH)
    buf = b""
    while b"\n" not in buf:
        buf += s.recv(4096)
    hello = json.loads(buf.split(b"\n", 1)[0])
    return (s, int(hello["cap_id"]))


def _claim(sock: socket.socket, cap: int) -> dict[str, Any]:
    return rt(
        sock,
        {"op": "claim", "urn": _URN, "mode": "exclusive", "holder": 1, "cap_id": cap},
    )


def _drain(sock: socket.socket, sub_id: str) -> list[dict[str, Any]]:
    r = rt(sock, {"op": "drain_events", "subscription_id": sub_id, "scope": "hw:/**"})
    assert r.get("ok") is True, r
    events: list[dict[str, Any]] = r["events"]
    # The whole point of the fold: no event may arrive unnamed.
    assert all(e.get("kind") != "other" for e in events), events
    return events


def test_another_operators_claim_arrives_as_named_ownership_changed(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """B claims; A — subscribed, never polling the backend — is told, by
    name, who now holds it."""
    sock_a, _cap = raw_broker_with_cap
    r = rt(sock_a, {"op": "subscribe", "id": "ev", "scope": "hw:/test/**"})
    assert r.get("ok") is True

    other, other_cap = _second_connection()
    try:
        claimed = _claim(other, other_cap)
        assert claimed.get("ok") is True, claimed

        events = _drain(sock_a, "ev")
        owned = [e for e in events if e["kind"] == "ownership_changed"]
        assert len(owned) == 1, events
        assert owned[0]["urn"] == _URN
        # A grant: nobody held it before, someone holds it now.
        assert owned[0]["old"] is None
        assert isinstance(owned[0]["new"], int)
        assert owned[0]["new"] > 0
    finally:
        other.close()


def test_a_release_arrives_as_the_mirror_image(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """The loss renders old:<holder>, new:null — the exact inverse of the
    grant, so a consumer can tell them apart without a re-poll."""
    sock_a, _cap = raw_broker_with_cap
    r = rt(sock_a, {"op": "subscribe", "id": "ev", "scope": "hw:/test/**"})
    assert r.get("ok") is True

    other, other_cap = _second_connection()
    try:
        claimed = _claim(other, other_cap)
        assert claimed.get("ok") is True, claimed
        lease = claimed["lease_id"]
        holder = _drain(sock_a, "ev")[0]["new"]

        released = rt(other, {"op": "release", "urn": _URN, "lease_id": lease})
        assert released.get("ok") is True, released

        events = _drain(sock_a, "ev")
        owned = [e for e in events if e["kind"] == "ownership_changed"]
        assert len(owned) == 1, events
        assert owned[0]["urn"] == _URN
        assert owned[0]["old"] == holder
        assert owned[0]["new"] is None
    finally:
        other.close()


def test_a_revoke_announces_the_loss_to_every_subscriber(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """A steal-gated revoke is an ownership fact ending like any other —
    the prior holder's loss is announced, not silently applied."""
    sock_a, cap = raw_broker_with_cap
    r = rt(sock_a, {"op": "subscribe", "id": "ev", "scope": "hw:/test/**"})
    assert r.get("ok") is True

    other, other_cap = _second_connection()
    try:
        claimed = _claim(other, other_cap)
        assert claimed.get("ok") is True, claimed
        holder = _drain(sock_a, "ev")[0]["new"]

        revoked = rt(sock_a, {"op": "revoke", "urn": _URN, "cap_id": cap})
        if revoked.get("ok") is not True:
            # The fake backend supports revoke; if a future build does not,
            # say so rather than passing vacuously.
            pytest.skip(f"revoke unsupported on this build: {revoked}")

        events = _drain(sock_a, "ev")
        owned = [e for e in events if e["kind"] == "ownership_changed"]
        assert len(owned) == 1, events
        assert owned[0]["old"] == holder
        assert owned[0]["new"] is None
    finally:
        other.close()


def test_no_lease_event_ever_renders_as_kind_other(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """The fold's negative pin, stated on its own so a regression names
    itself. Every kind the core emits — ownership_changed and faulted —
    must arrive with its real name; `{"kind":"other"}` is the exact
    dishonesty B1 removed, and `_drain` refuses it."""
    sock_a, _cap = raw_broker_with_cap
    r = rt(sock_a, {"op": "subscribe", "id": "ev", "scope": "hw:/**"})
    assert r.get("ok") is True

    other, other_cap = _second_connection()
    try:
        claimed = _claim(other, other_cap)
        assert claimed.get("ok") is True, claimed
        rt(other, {"op": "release", "urn": _URN, "lease_id": claimed["lease_id"]})

        events = _drain(sock_a, "ev")
        assert len(events) >= 2, events
        assert {e["kind"] for e in events} == {"ownership_changed"}, events
    finally:
        other.close()


def test_a_quarantine_arrives_as_named_faulted_with_its_reason_code(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """A restart reconcile quarantines every present-but-unclaimed device.
    Each of those is a real mutation, so each announces itself — by name,
    carrying WHY (the ``QuarantineReason`` discriminant), not merely THAT."""
    sock_a, cap = raw_broker_with_cap
    r = rt(sock_a, {"op": "subscribe", "id": "ev", "scope": "hw:/test/**"})
    assert r.get("ok") is True

    rec = rt(sock_a, {"op": "reconcile", "scope": "hw:/test/**", "cap_id": cap})
    assert rec.get("ok") is True, rec
    assert rec.get("quarantined", 0) > 0, rec

    events = _drain(sock_a, "ev")
    faulted = [e for e in events if e["kind"] == "faulted"]
    assert len(faulted) == rec["quarantined"], (rec, events)
    for e in faulted:
        assert e["urn"].startswith("hw:/test/")
        assert isinstance(e["code"], int)
    # `unclaimed_after_restart` is the 5th QuarantineReason (index 4); the
    # code is the discriminant, so it is stable for as long as the carved
    # enum's order is. A reorder there is a wire break and must show up here.
    assert {e["code"] for e in faulted} == {4}, events
