"""Regression pin for J1 (G5 / Cluster J — wave-2 follow-up).

**The bug.** `facade.release` returned `void` even when route(urn)
returned null (unrouted URN — no worker owns it). The server.zig
release handler emitted `{"ok":true}` regardless. A consumer
calling `Client.release("hw:/never-existed", lease=42)` would
get a silent success — exactly the wrong shape for a contract
where consumers rely on typed catch for "I released against the
wrong URN" diagnostics.

**The fix.** `facade.release` now returns `Err!void`. Route-miss
returns `Err.NotFound`; the server handler propagates via
`catch |e| break :blk .{ .resp = fmtErr(out, @errorName(e)) }`
so the wire emits `{"ok":false,"err":"NotFound"}` instead of
the silent `ok:true`.

**This pin** drives the wire directly + asserts the corrected
error shape. The contract test also verifies idempotency on a
ROUTED URN survives — releasing an unknown lease against a
known-routed URN MUST still succeed (Part 6.3 lease engine
contract: "release is total + idempotent for the caller").
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from tests.contract.conftest import rt

if TYPE_CHECKING:
    import socket

pytestmark = pytest.mark.contract


def test_release_unrouted_urn_returns_not_found(
    raw_broker: socket.socket,
) -> None:
    """Unrouted URN release MUST surface as NotFound on the wire,
    not silently report ok:true. J1 regression pin."""
    resp = rt(
        raw_broker,
        {
            "op": "release",
            "urn": "hw:/never-existed/dev/0",
            "lease_id": 42,
        },
    )
    assert resp.get("ok") is False, (
        f"J1 regression: unrouted release reported success. Got: {resp!r}"
    )
    assert resp.get("err") == "NotFound", f"Expected NotFound, got: {resp!r}"


def test_release_unknown_lease_on_routed_urn_still_idempotent(
    raw_broker: socket.socket,
) -> None:
    """Releasing an unknown LEASE against a ROUTED URN MUST still
    succeed (the lease engine's total+idempotent contract per
    Part 6.3 — "the post-condition that nobody holds this lease
    already holds"). J1's fix MUST NOT regress this path:
    NotFound is for ROUTE misses, not for missing-lease misses
    within a known worker."""
    # hw:/test/dev0 is a real URN on the seeded fake worker.
    resp = rt(
        raw_broker,
        {
            "op": "release",
            "urn": "hw:/test/dev0",
            "lease_id": 999_999,  # unknown lease id, but URN routes fine
        },
    )
    assert resp.get("ok") is True, (
        f"J1 regressed lease-engine idempotency: release on "
        f"routed URN with unknown lease should succeed. Got: {resp!r}"
    )


def test_release_truly_held_lease_still_succeeds(
    raw_broker_with_cap: tuple[socket.socket, int],
) -> None:
    """End-to-end happy path: claim a lease, then release it.
    Sanity check that the J1 fix doesn't break the canonical
    flow. Uses `raw_broker_with_cap` because the claim path on
    the seeded fake worker requires the broker's root cap_id."""
    sock, cap_id = raw_broker_with_cap
    claim_resp = rt(
        sock,
        {
            "op": "claim",
            "urn": "hw:/test/dev0",
            "mode": "exclusive",
            "holder": 1,
            "cap_id": cap_id,
        },
    )
    assert claim_resp.get("ok") is True, f"setup failed: {claim_resp!r}"
    lease_id = claim_resp["lease_id"]
    release_resp = rt(
        sock,
        {
            "op": "release",
            "urn": "hw:/test/dev0",
            "lease_id": lease_id,
        },
    )
    assert release_resp.get("ok") is True, f"holder release failed: {release_resp!r}"
