"""Hardware-passthrough-isolation checks (MALWARE_ANALYSIS.md §5.4, 3 checks).

Every ``<hostdev>`` element gives the guest direct hardware access
— a leakage path that defeats every other layer of isolation. All
three checks are BLOCKING + AUTO_FIX (the fix is "strip the
element").

Distinct from :mod:`.network`'s ``net_no_passthrough_nic``: that
check focuses on PCI hostdevs whose alias suggests they're NICs;
the ones here catch ALL PCI / USB / mdev passthroughs regardless
of their semantic role.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.vm.core.security.isolation.checks._helpers import finding
from mgf.vm.core.security.isolation.types import (
    FixKind,
    IsolationCategory,
    IsolationFinding,
    isolation_check,
)

if TYPE_CHECKING:
    from mgf.vm.core.security.isolation.context import AnalysisContext


__all__ = [
    "hostdev_no_mdev",
    "hostdev_no_pci",
    "hostdev_no_usb",
]


def _hostdevs_of_type(ctx: AnalysisContext, dev_type: str) -> list[str]:
    """Return the alias name (or empty) of each ``<hostdev>`` of *dev_type*."""
    out: list[str] = []
    for hostdev in ctx.find_devices("hostdev"):
        if hostdev.get("mode") != "subsystem":
            continue
        if hostdev.get("type") != dev_type:
            continue
        alias = hostdev.find("alias")
        out.append(alias.get("name", "") if alias is not None else "")
    return out


@isolation_check
def hostdev_no_pci(ctx: AnalysisContext) -> IsolationFinding:
    """No ``<hostdev mode='subsystem' type='pci'>``."""
    aliases = _hostdevs_of_type(ctx, "pci")
    return finding(
        fid="hostdev.no_pci",
        title="No PCI device passed through to guest",
        category=IsolationCategory.BLOCKING,
        fix_kind=FixKind.AUTO_FIX,
        passing=not aliases,
        summary_pass="no PCI hostdev declared",
        summary_fail=(
            f"{len(aliases)} PCI hostdev passthrough(s) — guest has "
            f"direct hardware access (DMA, MMIO, IRQs)"
        ),
        details={"aliases": aliases},
        fix_executor="strip.hostdev.pci",
    )


@isolation_check
def hostdev_no_usb(ctx: AnalysisContext) -> IsolationFinding:
    """No ``<hostdev mode='subsystem' type='usb'>``."""
    aliases = _hostdevs_of_type(ctx, "usb")
    return finding(
        fid="hostdev.no_usb",
        title="No USB device passed through to guest",
        category=IsolationCategory.BLOCKING,
        fix_kind=FixKind.AUTO_FIX,
        passing=not aliases,
        summary_pass="no USB hostdev declared",
        summary_fail=(
            f"{len(aliases)} USB hostdev passthrough(s) — guest has "
            f"direct device access; HID injection, mass storage, etc."
        ),
        details={"aliases": aliases},
        fix_executor="strip.hostdev.usb",
    )


@isolation_check
def hostdev_no_mdev(ctx: AnalysisContext) -> IsolationFinding:
    """No ``<hostdev mode='subsystem' type='mdev'>`` (mediated devices,
    typically vGPU partitions)."""
    aliases = _hostdevs_of_type(ctx, "mdev")
    return finding(
        fid="hostdev.no_mdev",
        title="No mediated device passed through",
        category=IsolationCategory.BLOCKING,
        fix_kind=FixKind.AUTO_FIX,
        passing=not aliases,
        summary_pass="no mdev hostdev declared",
        summary_fail=(
            f"{len(aliases)} mdev hostdev passthrough(s) — typically "
            f"vGPU partitions; shared hardware = side-channel surface"
        ),
        details={"aliases": aliases},
        fix_executor="strip.hostdev.mdev",
    )
