"""Host disk-image conformance scenarios (CONF-O-* in ``contracts.md`` §10).

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

from __future__ import annotations

from datetime import timedelta
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections.abc import Mapping

    from mgf.vm.core.contracts import HostDiskBackend


def disk_image_round_trip(
    adapter: HostDiskBackend,
    *,
    sources: Mapping[str, str],
    workdir: str,
) -> None:
    """CONF-O-01 — copy / overlay / archive→extract round-trip (Wave-R4).

    Drives the OPTIONAL :class:`HostDiskBackend` over a set of pre-existing
    source images (``sources`` maps guest target → host path the adapter can
    already read; the Fake seeds them, a real adapter arranges them). Asserts:

    * :meth:`image_info` returns a neutral :class:`DiskImageInfo` with a
      positive virtual size and a format.
    * :meth:`copy_image` produces an independent image of the same size.
    * :meth:`create_overlay` produces an image whose ``backing_file`` is the
      source.
    * :meth:`archive` produces a UTC-stamped :class:`BackupManifest` with one
      checksummed artifact per source and round-trips the opaque ``metadata``.
    * :meth:`extract` restores every disk and **preserves bytes + checksums**
      — proven by re-archiving the restored disks and comparing per-target
      SHA-256 to the original manifest — and round-trips ``metadata``.
    """
    if not sources:
        msg = "CONF-O-01 requires at least one source image"
        raise AssertionError(msg)
    base = workdir.rstrip("/")

    # image_info — neutral shape.
    for path in sources.values():
        info = adapter.image_info(path)
        assert info.virtual_size > 0, "virtual_size must be positive"
        assert info.format, "format must be reported"

    # copy_image — independent copy of the same virtual size.
    for i, (_target, path) in enumerate(sources.items()):
        src_info = adapter.image_info(path)
        dest = f"{base}/clone-{i}.img"
        adapter.copy_image(path, dest)
        copy_info = adapter.image_info(dest)
        assert copy_info.virtual_size == src_info.virtual_size
        assert copy_info.backing_file is None, "a full copy has no backing file"

    # create_overlay — overlay points back at the source.
    for i, (_target, path) in enumerate(sources.items()):
        overlay = f"{base}/overlay-{i}.qcow2"
        adapter.create_overlay(path, overlay)
        ov_info = adapter.image_info(overlay)
        assert ov_info.backing_file == path, "overlay backing_file must be the source"

    # archive — UTC-stamped manifest, one checksummed artifact per source.
    archive_path = f"{base}/backup.tar"
    manifest = adapter.archive(
        "conf-o-01-vm",
        dict(sources),
        archive_path,
        metadata={"conf": "o-01"},
    )
    assert manifest.created_at.tzinfo is not None, "created_at must be tz-aware (DP-12)"
    assert manifest.created_at.utcoffset() == timedelta(0), "created_at must be UTC"
    assert len(manifest.disks) == len(sources)
    assert {d.target for d in manifest.disks} == set(sources)
    assert all(d.sha256 for d in manifest.disks), "every artifact carries a checksum"
    assert dict(manifest.metadata) == {"conf": "o-01"}, "metadata must round-trip"
    original_sums = {d.target: d.sha256 for d in manifest.disks}

    # extract — restores every disk, round-trips metadata.
    restored = adapter.extract(archive_path, f"{base}/restore")
    assert {d.target for d in restored.disks} == set(sources)
    assert dict(restored.metadata) == {"conf": "o-01"}, "metadata must round-trip"

    # Byte/checksum preservation — re-archive the restored disks and compare
    # per-target SHA-256 to the original manifest (proven through the Protocol
    # surface, without reading raw bytes).
    re_manifest = adapter.archive(
        "conf-o-01-vm-rt",
        {d.target: d.path for d in restored.disks},
        f"{base}/backup-rt.tar",
    )
    restored_sums = {d.target: d.sha256 for d in re_manifest.disks}
    assert restored_sums == original_sums, "extract must preserve bytes + checksums"


__all__ = ["disk_image_round_trip"]
