"""Behaviour tests for :mod:`mgf.vm.core.configurator.types`."""

from __future__ import annotations

import dataclasses

import pytest

from mgf.vm.core.configurator.types import (
    ConfigChange,
    CPUConfig,
    DiskConfig,
    DisplayConfig,
    FirmwareConfig,
    MemoryConfig,
    Metadata,
    NICConfig,
    PCIDevice,
    ShareConfig,
    USBDevice,
    VMConfig,
)


class TestVMConfig:
    def test_round_trip(self) -> None:
        cpu = CPUConfig(vcpus=2, vcpus_max=4, model="host-passthrough")
        mem = MemoryConfig(current_mb=2048, max_mb=4096, hugepages=False)
        fw = FirmwareConfig(type="uefi", secure_boot=True)
        meta = Metadata(title="web", description="nginx front")
        disp = DisplayConfig(type="vnc", listen="127.0.0.1", port=None)
        disk = DiskConfig(
            target="vda",
            source="/var/lib/libvirt/images/web.qcow2",
            bus="virtio",
            format="qcow2",
        )
        nic = NICConfig(mac="52:54:00:aa:bb:cc", model="virtio", network="default")
        share = ShareConfig(driver="virtiofs", source="/srv", target="srv")
        pci = PCIDevice(domain=0, bus=1, slot=2, function=0)
        usb = USBDevice(vendor=0x1D6B, product=0x0002)

        config = VMConfig(
            name="web",
            uuid="fake-uuid",
            cpu=cpu,
            memory=mem,
            firmware=fw,
            metadata=meta,
            display=disp,
            disks=(disk,),
            nics=(nic,),
            shares=(share,),
            pci_devices=(pci,),
            usb_devices=(usb,),
        )
        assert config.cpu.vcpus == 2
        assert config.memory.max_mb == 4096
        assert config.nics[0].mac == "52:54:00:aa:bb:cc"
        assert config.nics[0].network == "default"
        assert config.disks[0].format == "qcow2"
        assert config.shares[0].driver == "virtiofs"
        assert config.pci_devices[0].slot == 2
        assert config.usb_devices[0].vendor == 0x1D6B
        assert config.firmware.secure_boot

    def test_collection_defaults_are_empty_tuples(self) -> None:
        config = VMConfig(
            name="bare",
            uuid="u",
            cpu=CPUConfig(vcpus=1, vcpus_max=1),
            memory=MemoryConfig(current_mb=512, max_mb=512),
            firmware=FirmwareConfig(type="bios"),
            metadata=Metadata(),
            display=DisplayConfig(type="none"),
        )
        assert config.disks == ()
        assert config.nics == ()
        assert config.shares == ()
        assert config.pci_devices == ()
        assert config.usb_devices == ()

    def test_value_equality(self) -> None:
        a = CPUConfig(vcpus=2, vcpus_max=4, model="host")
        b = CPUConfig(vcpus=2, vcpus_max=4, model="host")
        c = CPUConfig(vcpus=2, vcpus_max=8, model="host")
        assert a == b
        assert a != c

    def test_immutable(self) -> None:
        cpu = CPUConfig(vcpus=1, vcpus_max=2)
        with pytest.raises(dataclasses.FrozenInstanceError):
            cpu.vcpus = 4  # type: ignore[misc]


class TestFieldDefaults:
    def test_cpu_model_defaults_none(self) -> None:
        assert CPUConfig(vcpus=1, vcpus_max=1).model is None

    def test_memory_hugepages_defaults_false(self) -> None:
        assert MemoryConfig(current_mb=1, max_mb=1).hugepages is False

    def test_disk_readonly_defaults_false(self) -> None:
        disk = DiskConfig(target="vda", source="/d.img", bus="virtio", format="raw")
        assert disk.readonly is False

    def test_nic_optional_fields_default_none(self) -> None:
        nic = NICConfig(mac="52:54:00:00:00:01", model="virtio")
        assert nic.network is None
        assert nic.bridge is None

    def test_share_readonly_defaults_false(self) -> None:
        share = ShareConfig(driver="9p", source="/s", target="s")
        assert share.readonly is False

    def test_display_optional_fields_default_none(self) -> None:
        disp = DisplayConfig(type="spice")
        assert disp.listen is None
        assert disp.port is None

    def test_firmware_secure_boot_defaults_false(self) -> None:
        assert FirmwareConfig(type="bios").secure_boot is False

    def test_metadata_defaults_none(self) -> None:
        meta = Metadata()
        assert meta.title is None
        assert meta.description is None

    def test_passthrough_devices_carry_addresses(self) -> None:
        pci = PCIDevice(domain=0, bus=3, slot=0, function=1)
        usb = USBDevice(vendor=0x046D, product=0xC52B)
        assert (pci.domain, pci.bus, pci.slot, pci.function) == (0, 3, 0, 1)
        assert (usb.vendor, usb.product) == (0x046D, 0xC52B)


class TestConfigChange:
    def test_fields(self) -> None:
        change = ConfigChange(
            subsystem="cpu",
            summary="vcpus 2 -> 4",
            old_xml="<domain/>",
            new_xml="<domain/>",
        )
        assert change.subsystem == "cpu"
        assert change.summary == "vcpus 2 -> 4"
        assert change.old_xml == "<domain/>"
        assert change.requires_shutdown is False

    def test_requires_shutdown(self) -> None:
        change = ConfigChange(
            subsystem="firmware",
            summary="bios -> uefi",
            old_xml="",
            new_xml="",
            requires_shutdown=True,
        )
        assert change.requires_shutdown

    def test_immutable(self) -> None:
        change = ConfigChange(subsystem="cpu", summary="s", old_xml="", new_xml="")
        with pytest.raises(dataclasses.FrozenInstanceError):
            change.subsystem = "memory"  # type: ignore[misc]
