"""Shared helpers for UKO Layer 2 BDD step files.

Provides a ``capture_error`` context manager that reduces the
repeated try/except boilerplate for "negative-test" steps.

Used by:
- uko_l2_vocabulary_steps.py
- uko_l2_detail_level_steps.py
- uko_l2_coverage_ttl_steps.py
- uko_l2_vocab_registry_steps.py
"""

from __future__ import annotations

from collections.abc import Generator
from contextlib import contextmanager
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from behave.runner import Context


@contextmanager
def capture_error(
    ctx: Context,
    *exc_types: type[BaseException],
) -> Generator[None]:
    """Run the body and capture any matching exception on ``ctx.error``.

    On success ``ctx.error`` is set to ``None``; on failure it is set
    to the caught exception instance.

    Usage::

        with capture_error(ctx, ValidationError):
            VocabularyClass(uri="", label="Test")

    Args:
        ctx: Behave context (must support attribute assignment).
        *exc_types: One or more exception types to catch.

    Yields:
        Control to the step body.

    Note:
        This intentionally suppresses the caught exception so the
        subsequent ``@then`` step can inspect ``ctx.error``.  This is
        a **test-infrastructure exemption** from CONTRIBUTING.md's
        "do not suppress errors" rule -- suppression is the entire
        purpose of negative-test capture in BDD step definitions.
    """
    try:
        yield
        ctx.error = None  # type: ignore[attr-defined]
    except exc_types as exc:
        ctx.error = exc  # type: ignore[attr-defined]
