"""CPU + firmware isolation checks (MALWARE_ANALYSIS.md §5.6, 3 checks).

- :func:`cpu_no_host_passthrough` — ``<cpu mode>`` is not
  ``host-passthrough``. Passthrough exposes every quirky host-CPU
  feature to the guest; ``host-model`` is a portable subset.
- :func:`firmware_uefi_secureboot` — OVMF Secure Boot enabled.
  Reduces malware persistence options at the firmware layer.
- :func:`cpu_no_topology_leak` — no ``<cpu><topology>`` element
  matching the host's exact topology. A guest seeing the host's
  exact socket/core layout is a fingerprinting + side-channel
  surface.
"""

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__ = [
    "cpu_no_host_passthrough",
    "cpu_no_topology_leak",
    "firmware_uefi_secureboot",
]


@isolation_check
def cpu_no_host_passthrough(ctx: AnalysisContext) -> IsolationFinding:
    """``<cpu mode>`` is not ``host-passthrough``."""
    cpu = ctx.root().find("cpu")
    mode = cpu.get("mode", "") if cpu is not None else ""
    bad = mode == "host-passthrough"
    return finding(
        fid="cpu.no_host_passthrough",
        title="CPU mode is not 'host-passthrough'",
        category=IsolationCategory.ADVISORY,
        fix_kind=FixKind.AUTO_FIX,
        passing=not bad,
        summary_pass=f"CPU mode is {mode!r}" if mode else "no CPU element",
        summary_fail=(
            "CPU mode='host-passthrough' exposes every host CPU feature "
            "(model, microcode quirks, capability bits) to the guest"
        ),
        details={"mode": mode},
        fix_executor="cpu.swap_to_host_model",
    )


@isolation_check
def firmware_uefi_secureboot(ctx: AnalysisContext) -> IsolationFinding:
    """OVMF Secure Boot enabled.

    Detected by either ``<os firmware='efi'>`` (modern attribute)
    OR an explicit ``<loader>`` whose path contains ``OVMF`` AND a
    ``<secure>`` attribute or feature flag.
    """
    os_block = ctx.root().find("os")
    if os_block is None:
        return finding(
            fid="firmware.uefi_secureboot",
            title="OVMF Secure Boot enabled",
            category=IsolationCategory.ADVISORY,
            fix_kind=FixKind.MANUAL,
            passing=False,
            summary_pass="",
            summary_fail="no <os> element — firmware posture unknown",
            details={},
        )

    has_efi_attr = os_block.get("firmware") == "efi"
    loader = os_block.find("loader")
    has_ovmf_loader = loader is not None and "OVMF" in (loader.text or "")
    secure = (loader.get("secure") if loader is not None else None) == "yes"

    is_uefi = has_efi_attr or has_ovmf_loader
    is_secureboot = is_uefi and (has_efi_attr or secure)
    return finding(
        fid="firmware.uefi_secureboot",
        title="OVMF Secure Boot enabled",
        category=IsolationCategory.ADVISORY,
        fix_kind=FixKind.MANUAL,  # rebuild image with UEFI vars
        passing=is_secureboot,
        summary_pass=(
            "UEFI firmware with Secure Boot detected "
            f"({'firmware=efi' if has_efi_attr else 'OVMF + secure=yes'})"
        ),
        summary_fail=(
            "firmware is BIOS" if not is_uefi else "UEFI present but Secure Boot not detected"
        ),
        details={
            "firmware_attr": os_block.get("firmware", ""),
            "has_loader": loader is not None,
        },
    )


@isolation_check
def cpu_no_topology_leak(ctx: AnalysisContext) -> IsolationFinding:
    """No ``<cpu><topology>`` element disclosed to the guest.

    A spec that doesn't pin topology lets qemu default to
    1-socket-N-cores, which is generic. An explicit topology
    matching the host fingerprints the host.
    """
    cpu = ctx.root().find("cpu")
    topology = cpu.find("topology") if cpu is not None else None
    has_topology = topology is not None

    return finding(
        fid="cpu.no_topology_leak",
        title="CPU topology not pinned to host layout",
        category=IsolationCategory.ADVISORY,
        fix_kind=FixKind.AUTO_FIX,
        passing=not has_topology,
        summary_pass="no <cpu><topology> element — qemu defaults to generic 1-socket",
        summary_fail=(
            "<cpu><topology> element present — verify it doesn't expose "
            "the host's exact socket/core layout to the guest"
        ),
        details={
            "sockets": topology.get("sockets") if topology is not None else None,
            "cores": topology.get("cores") if topology is not None else None,
            "threads": topology.get("threads") if topology is not None else None,
        },
        fix_executor="cpu.generic_topology",
    )
