"""Virtual-network management conformance scenarios (CONF-N-* §10).

Exercise the OPTIONAL :class:`~mgf.vm.core.contracts.NetworkBackend` Protocol
(D-G15). Each scenario is a callable taking a ``NetworkBackend`` instance and
either passing (return) or failing (raise).

These verify *management* by **observing** its effects through the
:class:`~mgf.vm.core.contracts.HypervisorBackend` the management backend shares
an engine connection with — the same split D-G15 ratified (observation on the
hypervisor, management here).
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.vm.core.contracts import NetworkSpec
from mgf.vm.core.exceptions import NetworkNotFound

if TYPE_CHECKING:
    from mgf.vm.core.contracts import (
        HypervisorBackend,
        NetworkBackend,
        NetworkProfile,
    )


def network_management_round_trip(
    adapter: NetworkBackend,
    *,
    observer: HypervisorBackend,
    name: str = "conf-n-01-net",
) -> None:
    """CONF-N-01 — define → start → stop → delete round-trip (Wave-R3, D-G15).

    Drives the OPTIONAL :class:`NetworkBackend` and asserts each management op
    is visible through ``observer.list_networks`` (D-G14):

    * :meth:`define_network` makes the network observable, **inactive**.
    * :meth:`start_network` activates it (observed ``active`` True).
    * :meth:`stop_network` deactivates it.
    * :meth:`delete_network` removes it from the observed list, and is
      idempotent (deleting it again is a no-op success).
    * :meth:`start_network` on an unknown network raises ``NetworkNotFound``.
    """
    spec = NetworkSpec(name=name, forward_mode="nat", cidr="192.168.211.0/24")

    def _observed() -> NetworkProfile | None:
        for profile in observer.list_networks():
            if profile.name == name:
                return profile
        return None

    # define — observable + inactive.
    defined = adapter.define_network(spec)
    assert defined.name == name, "define_network returns the defined profile"
    obs = _observed()
    assert obs is not None, "a defined network must be observable via list_networks"
    assert obs.active is False, "a freshly-defined network is inactive"

    # start — activates.
    adapter.start_network(name)
    started = _observed()
    assert started is not None and started.active is True, "start_network activates"

    # stop — deactivates.
    adapter.stop_network(name)
    stopped = _observed()
    assert stopped is not None and stopped.active is False, "stop_network deactivates"

    # delete — removed, and idempotent.
    adapter.delete_network(name)
    assert _observed() is None, "delete_network removes the network"
    adapter.delete_network(name)  # idempotent: already-absent is success

    # start on an unknown network raises NetworkNotFound.
    raised = False
    try:
        adapter.start_network("conf-n-01-no-such-network")
    except NetworkNotFound:
        raised = True
    assert raised, "start_network(unknown) must raise NetworkNotFound"


__all__ = ["network_management_round_trip"]
