"""Device conformance scenarios (CONF-D-* in ``contracts.md`` §10)."""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.vm.core.exceptions import LeaseExpired

if TYPE_CHECKING:
    from mgf.vm.core.contracts import DeviceBackend


def claim_assignment_release_round_trip(adapter: DeviceBackend) -> None:
    """CONF-D-01 — basic claim→assignment_for→release happy path."""
    assignable = adapter.list_assignable()
    assert assignable, "device adapter must report at least one assignable device"
    target = next((d for d in assignable if d.assignable), None)
    assert target is not None, "at least one device must be assignable"

    lease = adapter.claim(target.device_id)
    try:
        assignment = adapter.assignment_for(lease)
        assert assignment.lease == lease
        assert assignment.device_id == target.device_id
        assert assignment.host_handle, "host_handle must be non-empty"
    finally:
        adapter.release(lease)


def fenced_lease_assignment_raises(adapter: DeviceBackend) -> None:
    """CONF-D-04 — client-death fence simulation: a fenced lease's
    ``assignment_for`` raises ``LeaseExpired``.

    Skipped on adapters whose backend doesn't simulate fencing
    (capability ``fenced_by_liveness=False``) — e.g., a hypothetical
    Windows DDA adapter. The fake exposes a ``fence_lease`` helper
    for this purpose; real adapters' integration tests inject a
    crash via a separate harness.
    """
    caps = adapter.capabilities()
    if not caps.fenced_by_liveness:
        from mgf.vm.core.conformance.runner import _SkipScenario

        msg = "adapter reports fenced_by_liveness=False"
        raise _SkipScenario(msg)

    # The fake exposes fence_lease; other adapters must arrange the
    # equivalent (the runner re-tries through their integration harness).
    fence = getattr(adapter, "fence_lease", None)
    if fence is None:
        from mgf.vm.core.conformance.runner import _SkipScenario

        msg = (
            "adapter does not expose fence_lease() — integration harness "
            "must simulate client-death externally"
        )
        raise _SkipScenario(msg)

    assignable = adapter.list_assignable()
    target = next((d for d in assignable if d.assignable), None)
    assert target is not None
    lease = adapter.claim(target.device_id)
    fence(lease)
    raised = False
    try:
        adapter.assignment_for(lease)
    except LeaseExpired:
        raised = True
    assert raised, "assignment_for(fenced_lease) must raise LeaseExpired"


__all__ = [
    "claim_assignment_release_round_trip",
    "fenced_lease_assignment_raises",
]
