"""Disk-attribute fix executors.

- :func:`disk_set_cache_none` — set ``cache='none'`` on every
  ``<driver>`` of a ``<disk>``.
- :func:`disk_set_discard_ignore` — set ``discard='ignore'`` on
  every disk driver (or strip ``discard`` to let libvirt default
  to ignore).
"""

from __future__ import annotations

import xml.etree.ElementTree as ET
from typing import TYPE_CHECKING, Any

from mgf.vm.core.security.isolation.fixes._helpers import devices_element
from mgf.vm.core.security.isolation.types import isolation_fix

if TYPE_CHECKING:
    from mgf.vm.core.common.progress import ProgressReporter


__all__ = ["disk_set_cache_none", "disk_set_discard_ignore"]


def _set_disk_driver_attr(
    target: ET.ElementTree,
    *,
    attr: str,
    value: str,
    reporter: ProgressReporter,
    dry_run: bool,
    fix_id: str,
) -> None:
    devices = devices_element(target)
    if devices is None:
        reporter.report(f"{fix_id}: no <devices> element")
        return

    changed = 0
    for disk in devices.findall("disk"):
        driver = disk.find("driver")
        if driver is None:
            driver = ET.SubElement(disk, "driver", attrib={"name": "qemu"})
        if driver.get(attr) == value:
            continue
        if dry_run:
            target_elem = disk.find("target")
            tgt_dev = target_elem.get("dev", "?") if target_elem is not None else "?"
            reporter.report(
                f"{fix_id}: (dry-run) would set {attr}={value!r} on {tgt_dev}",
            )
        else:
            driver.set(attr, value)
        changed += 1

    if changed == 0:
        reporter.report(f"{fix_id}: already satisfied")
        return
    if not dry_run:
        reporter.report(f"{fix_id}: set {attr}={value!r} on {changed} disk(s)")


@isolation_fix("disk.set_cache_none")
def disk_set_cache_none(
    *,
    target: ET.ElementTree,
    reporter: ProgressReporter,
    dry_run: bool = False,
    **_kw: Any,
) -> None:
    """Set ``cache='none'`` on every disk driver."""
    _set_disk_driver_attr(
        target,
        attr="cache",
        value="none",
        reporter=reporter,
        dry_run=dry_run,
        fix_id="disk.set_cache_none",
    )


@isolation_fix("disk.set_discard_ignore")
def disk_set_discard_ignore(
    *,
    target: ET.ElementTree,
    reporter: ProgressReporter,
    dry_run: bool = False,
    **_kw: Any,
) -> None:
    """Set ``discard='ignore'`` on every disk driver."""
    _set_disk_driver_attr(
        target,
        attr="discard",
        value="ignore",
        reporter=reporter,
        dry_run=dry_run,
        fix_id="disk.set_discard_ignore",
    )
