"""Common helpers used by every isolation check module.

Two main shapes:

- :func:`finding` — build an :class:`IsolationFinding` with the
  passing/failing summaries computed from a single template.
- Element-walk helpers wrapping :class:`AnalysisContext`.

Keeping these here means each per-category check module can focus
on the ID + the predicate; boilerplate stays out.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.vm.core.security.isolation.types import (
    FixKind,
    IsolationCategory,
    IsolationFinding,
)

if TYPE_CHECKING:
    from collections.abc import Mapping
    from typing import Any
    from xml.etree.ElementTree import Element


__all__ = [
    "finding",
    "first_attr",
]


def finding(
    *,
    fid: str,
    title: str,
    category: IsolationCategory,
    fix_kind: FixKind,
    passing: bool,
    summary_pass: str,
    summary_fail: str,
    details: Mapping[str, Any] | None = None,
    fix_executor: str = "",
) -> IsolationFinding:
    """Build an :class:`IsolationFinding` with a passing/failing template.

    The :attr:`IsolationFinding.fix_executor` field is dropped from
    passing findings so a clean run never carries phantom executor
    references — keeps the report YAML / JSON output tidy.
    """
    return IsolationFinding(
        id=fid,
        title=title,
        summary=summary_pass if passing else summary_fail,
        category=category,
        fix_kind=fix_kind,
        passing=passing,
        details=dict(details) if details else {},
        fix_executor="" if passing else fix_executor,
    )


def first_attr(element: Element, attr: str) -> str | None:
    """Return ``element.get(attr)`` as a non-empty string, else None."""
    val = element.get(attr)
    if val is None or val == "":
        return None
    return val
