"""Transport-neutral request shapes for reads + force-flag mutations.

:meth:`~mgf.vm.sdk.transport.base.Transport.read` and
:meth:`~mgf.vm.sdk.transport.base.Transport.submit_op` are type-erased
(``query: object`` / ``payload: object``) so the seam stays wire-neutral. The
client builds one of these tiny request markers; each transport interprets
them in its own terms (the in-process transport pattern-matches and routes to a
backend; the Phase-1 gRPC transport maps them to proto requests).

They live here — not in :mod:`mgf.vm.sdk.transport.inprocess` — so the client
stays decoupled from any concrete transport (the client imports *only* the
seam + these markers; it never imports a transport implementation).
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from mgf.vm.core.contracts import (
        MigrationPlan,
        MigrationTarget,
        PoolSpec,
        VolumeSpec,
    )
    from mgf.vm.core.ids import DeviceId, LeaseId, SnapshotId, VmId, VolumeId

__all__ = [
    "BackupVmPayload",
    "BridgeForDomain",
    "Capabilities",
    "ClaimDevicePayload",
    "CloneVmPayload",
    "ConvertFormatPayload",
    "CreatePoolPayload",
    "CreateVolumePayload",
    "DeletePayload",
    "DeletePoolPayload",
    "DeleteVolumePayload",
    "DeleteVolumeSnapshotPayload",
    "DetachDevicePayload",
    "DeviceCapabilities",
    "GetAutostart",
    "GetDevice",
    "GetGuestInterfaces",
    "GetGuestOsInfo",
    "GetPool",
    "GetVm",
    "GetVolume",
    "GuestPing",
    "ListDevices",
    "ListLeases",
    "ListNetworks",
    "ListOperations",
    "ListPools",
    "ListVmSnapshots",
    "ListVms",
    "ListVolumeSnapshots",
    "ListVolumes",
    "MigrateVmPayload",
    "NetworkLeasesQuery",
    "PowerPayload",
    "RefreshPool",
    "ReleaseDevicePayload",
    "RenamePayload",
    "ResizeVolumePayload",
    "RestoreVmPayload",
    "RevertVolumeSnapshotPayload",
    "SampleMetrics",
    "SetAutostartPayload",
    "SnapshotVolumePayload",
    "StorageCapabilities",
    "TransferLeasePayload",
    "VmAttachDevicePayload",
    "VmDeleteSnapshotPayload",
    "VmRevertSnapshotPayload",
    "VmSnapshotPayload",
]


# -- reads -----------------------------------------------------------------


@dataclass(frozen=True, slots=True)
class GetVm:
    """``read`` query for ``GetVm`` — one VM by id."""

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class ListVms:
    """``read`` query for ``ListVms`` — all VMs."""


@dataclass(frozen=True, slots=True)
class Capabilities:
    """``read`` query for hypervisor capabilities."""


@dataclass(frozen=True, slots=True)
class StorageCapabilities:
    """``read`` query for storage-backend capabilities."""


@dataclass(frozen=True, slots=True)
class DeviceCapabilities:
    """``read`` query for device-backend capabilities."""


@dataclass(frozen=True, slots=True)
class GetVolume:
    """``read`` query for ``GetVolume`` — one volume by id."""

    volume_id: VolumeId


@dataclass(frozen=True, slots=True)
class ListVolumes:
    """``read`` query for ``ListVolumes`` — volumes in a pool."""

    pool: str


@dataclass(frozen=True, slots=True)
class GetPool:
    """``read`` query for ``GetPool`` — one pool by name."""

    name: str


@dataclass(frozen=True, slots=True)
class ListPools:
    """``read`` query for ``ListPools`` — all storage pools."""


@dataclass(frozen=True, slots=True)
class RefreshPool:
    """``read`` query for ``RefreshPool`` — rescan one pool, return its
    fresh info (D-G20)."""

    name: str


@dataclass(frozen=True, slots=True)
class ListVmSnapshots:
    """``read`` query for ``ListSnapshots`` — VM snapshots."""

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class ListVolumeSnapshots:
    """``read`` query for volume snapshots."""

    volume_id: VolumeId


@dataclass(frozen=True, slots=True)
class GetDevice:
    """``read`` query for ``GetDevice`` — one host device by id."""

    device_id: DeviceId


@dataclass(frozen=True, slots=True)
class ListDevices:
    """``read`` query for ``ListDevices`` — assignable host devices."""


@dataclass(frozen=True, slots=True)
class ListLeases:
    """``read`` query for currently-held device leases."""


@dataclass(frozen=True, slots=True)
class ListOperations:
    """``read`` query for ``ListOperations`` — every known operation handle."""


# -- Wave-B observation reads (D-G13 / D-G14) ------------------------------


@dataclass(frozen=True, slots=True)
class ListNetworks:
    """``read`` query for ``list_networks`` — host virtual networks (D-G14)."""


@dataclass(frozen=True, slots=True)
class NetworkLeasesQuery:
    """``read`` query for ``network_leases`` — DHCP leases on a network (D-G14)."""

    network: str


@dataclass(frozen=True, slots=True)
class BridgeForDomain:
    """``read`` query for ``bridge_for_domain`` — a VM's host bridge (D-G14)."""

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class SampleMetrics:
    """``read`` query for ``sample_metrics`` — one per-domain sample (D-G13).

    Routed to the injected :class:`~mgf.vm.core.contracts.MetricsBackend`; the
    transport raises a typed :class:`~mgf.vm.core.exceptions.TransportError` when
    no metrics backend is configured (mirrors the device-read path).
    """

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class GetAutostart:
    """``read`` query for ``get_autostart`` — the host-boot autostart flag (D-G25).

    Routed to the injected :class:`~mgf.vm.core.contracts.DomainPolicyBackend`;
    the transport raises a typed :class:`~mgf.vm.core.exceptions.TransportError`
    when no policy backend is configured (mirrors the metrics-read path).
    """

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class GuestPing:
    """``read`` query for ``guest_ping`` — in-guest agent liveness (D-G19).

    Routed to the injected :class:`~mgf.vm.core.contracts.GuestAgentBackend`;
    the transport raises a typed ``TransportError`` when no guest-agent backend
    is configured (mirrors the metrics-read path).
    """

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class GetGuestOsInfo:
    """``read`` query for ``guest_os_info`` — guest OS identification (D-G19)."""

    vm_id: VmId


@dataclass(frozen=True, slots=True)
class GetGuestInterfaces:
    """``read`` query for ``guest_interfaces`` — guest-reported NICs (D-G19)."""

    vm_id: VmId


# -- mutation payloads carrying a flag -------------------------------------


@dataclass(frozen=True, slots=True)
class PowerPayload:
    """``submit_op`` payload for force-capable power ops (``stop`` / ``reboot``)."""

    vm_id: VmId
    force: bool = False


@dataclass(frozen=True, slots=True)
class SetAutostartPayload:
    """``submit_op`` payload for ``VM_SET_AUTOSTART`` — toggle the host-boot
    autostart flag (D-G25). Routed to the injected ``DomainPolicyBackend``."""

    vm_id: VmId
    enabled: bool


@dataclass(frozen=True, slots=True)
class DeletePayload:
    """``submit_op`` payload for ``VM_UNDEFINE``."""

    vm_id: VmId
    delete_storage: bool = False


@dataclass(frozen=True, slots=True)
class RenamePayload:
    """``submit_op`` payload for ``VM_RENAME`` — rename a stopped VM (D-G26)."""

    vm_id: VmId
    new_name: str


@dataclass(frozen=True, slots=True)
class MigrateVmPayload:
    """``submit_op`` payload for ``VM_MIGRATE`` (``hypervisor.migrate``).

    Carries the migration target plus the CP's pre-computed
    :class:`~mgf.vm.core.contracts.MigrationPlan` recipe the adapter executes.
    """

    vm_id: VmId
    dest: MigrationTarget
    plan: MigrationPlan


# -- VM snapshot payloads (VM_SNAPSHOT / _REVERT / _DELETE) -----------------


@dataclass(frozen=True, slots=True)
class VmSnapshotPayload:
    """``submit_op`` payload for ``VM_SNAPSHOT``."""

    vm_id: VmId
    name: str
    include_memory: bool = False


@dataclass(frozen=True, slots=True)
class VmRevertSnapshotPayload:
    """``submit_op`` payload for ``VM_REVERT_SNAPSHOT``."""

    vm_id: VmId
    snapshot_id: SnapshotId


@dataclass(frozen=True, slots=True)
class VmDeleteSnapshotPayload:
    """``submit_op`` payload for ``VM_DELETE_SNAPSHOT``."""

    vm_id: VmId
    snapshot_id: SnapshotId


# -- host disk-image payloads (VM_BACKUP / VM_CLONE / VM_RESTORE; D-G18) ----


@dataclass(frozen=True, slots=True)
class BackupVmPayload:
    """``submit_op`` payload for ``VM_BACKUP``.

    ``dest`` is the host path of the archive to write. The transport reads the
    source VM's spec, archives its disk images via the ``HostDiskBackend``, and
    carries the serialized ``VmSpec`` in the manifest metadata so a restore is
    self-contained.
    """

    vm_id: VmId
    dest: str


@dataclass(frozen=True, slots=True)
class CloneVmPayload:
    """``submit_op`` payload for ``VM_CLONE``.

    Clones the source VM into a new VM named ``new_name``, copying (full) or
    overlaying (``linked``) each disk image into ``dest_dir`` and defining the
    new VM from a spec with the new disk sources — engine-neutral, no native
    format rewrite (D-G18). ``dest_dir`` is a host directory the clone's images
    are written under.
    """

    vm_id: VmId
    new_name: str
    dest_dir: str
    linked: bool = False


@dataclass(frozen=True, slots=True)
class RestoreVmPayload:
    """``submit_op`` payload for ``VM_RESTORE``.

    Extracts ``archive`` into ``dest_dir`` (verifying checksums), reconstructs
    the ``VmSpec`` carried in the manifest metadata, repoints its disk sources
    at the restored images, and defines the VM. ``new_name`` overrides the
    backed-up VM name when restoring alongside the original.
    """

    archive: str
    dest_dir: str
    new_name: str | None = None


# -- device passthrough payloads (VM_ATTACH/DETACH_DEVICE) -----------------


@dataclass(frozen=True, slots=True)
class VmAttachDevicePayload:
    """``submit_op`` payload for ``VM_ATTACH_DEVICE`` (the attach transaction)."""

    vm_id: VmId
    device_id: DeviceId
    live: bool = True


@dataclass(frozen=True, slots=True)
class DetachDevicePayload:
    """``submit_op`` payload for ``VM_DETACH_DEVICE``."""

    vm_id: VmId
    device_id: DeviceId
    live: bool = True


# -- storage volume + pool payloads ----------------------------------------


@dataclass(frozen=True, slots=True)
class CreateVolumePayload:
    """``submit_op`` payload for ``VOLUME_CREATE``."""

    spec: VolumeSpec


@dataclass(frozen=True, slots=True)
class ResizeVolumePayload:
    """``submit_op`` payload for ``VOLUME_RESIZE``."""

    volume_id: VolumeId
    new_size_bytes: int


@dataclass(frozen=True, slots=True)
class DeleteVolumePayload:
    """``submit_op`` payload for ``VOLUME_DELETE``."""

    volume_id: VolumeId


@dataclass(frozen=True, slots=True)
class SnapshotVolumePayload:
    """``submit_op`` payload for ``VOLUME_SNAPSHOT``."""

    volume_id: VolumeId
    name: str


@dataclass(frozen=True, slots=True)
class RevertVolumeSnapshotPayload:
    """``submit_op`` payload for ``VOLUME_REVERT``."""

    volume_id: VolumeId
    snapshot_id: SnapshotId


@dataclass(frozen=True, slots=True)
class DeleteVolumeSnapshotPayload:
    """``submit_op`` payload for deleting a volume snapshot."""

    snapshot_id: SnapshotId


@dataclass(frozen=True, slots=True)
class ConvertFormatPayload:
    """``submit_op`` payload for ``VOLUME_CONVERT``."""

    src: VolumeId
    dst_format: str
    dst_spec: VolumeSpec


@dataclass(frozen=True, slots=True)
class CreatePoolPayload:
    """``submit_op`` payload for ``POOL_CREATE``."""

    spec: PoolSpec


@dataclass(frozen=True, slots=True)
class DeletePoolPayload:
    """``submit_op`` payload for ``POOL_DELETE``."""

    name: str
    force: bool = False


# -- device lease payloads (DEVICE_CLAIM / _RELEASE / _TRANSFER) -----------


@dataclass(frozen=True, slots=True)
class ClaimDevicePayload:
    """``submit_op`` payload for ``DEVICE_CLAIM``."""

    device_id: DeviceId


@dataclass(frozen=True, slots=True)
class ReleaseDevicePayload:
    """``submit_op`` payload for ``DEVICE_RELEASE``."""

    lease_id: LeaseId


@dataclass(frozen=True, slots=True)
class TransferLeasePayload:
    """``submit_op`` payload for ``DEVICE_TRANSFER``."""

    lease_id: LeaseId
    dest_host: str
