"""Step definitions for actor_config_coverage_boost.feature.

Targets uncovered lines in src/cleveragents/actor/config.py:
  - Lines 50-54: load_yaml_text JSON else-branch (null, non-dict, valid dict)
  - Line 127: _load_v2_yaml_content defensive check after interpolation
"""

from __future__ import annotations

from unittest.mock import patch

from behave import given, then, when  # type: ignore[import-untyped]
from behave.runner import Context  # type: ignore[import-untyped]

from cleveragents.actor.config import ActorConfiguration

# Module-level variable to hold the mock patch (avoids behave Context delattr issues)
_active_patch = None


# ── load_yaml_text: JSON null → empty dict (lines 50-51) ────────────


@when('I call load_yaml_text with "null"')
def step_load_yaml_text_null(context: Context) -> None:
    context.yaml_result = ActorConfiguration.load_yaml_text("null")


@then("the load_yaml_text result should be an empty dict")
def step_assert_yaml_result_empty_dict(context: Context) -> None:
    assert context.yaml_result == {}, (
        f"Expected empty dict, got {context.yaml_result!r}"
    )


# ── load_yaml_text: non-dict JSON values raise ValueError (lines 52-53) ──


@when("I call load_yaml_text with a JSON string value")
def step_load_yaml_text_json_string(context: Context) -> None:
    context.caught_error = None
    try:
        ActorConfiguration.load_yaml_text('"just a string"')
    except ValueError as exc:
        context.caught_error = exc


@when("I call load_yaml_text with a JSON array value")
def step_load_yaml_text_json_array(context: Context) -> None:
    context.caught_error = None
    try:
        ActorConfiguration.load_yaml_text("[1, 2, 3]")
    except ValueError as exc:
        context.caught_error = exc


@when("I call load_yaml_text with a JSON integer value")
def step_load_yaml_text_json_integer(context: Context) -> None:
    context.caught_error = None
    try:
        ActorConfiguration.load_yaml_text("42")
    except ValueError as exc:
        context.caught_error = exc


# ── Shared assertion: reuses the same pattern as bridge_remaining_coverage_steps.py
#    (uses context.caught_error).  The step text is already registered by that file
#    in the full suite; this definition is kept here so the feature can also run
#    standalone in an isolated runner.  behave-parallel (nox) loads steps per-worker
#    so no conflict arises at runtime.


@then('a ValueError with message containing "{fragment}" should be raised')
def step_assert_caught_valueerror_fragment(context: Context, fragment: str) -> None:
    assert context.caught_error is not None, "Expected a ValueError but none was raised"
    assert fragment.lower() in str(context.caught_error).lower(), (
        f"Expected '{fragment}' in error message: {context.caught_error}"
    )


# ── load_yaml_text: valid JSON dict (line 54) ───────────────────────


@when("I call load_yaml_text with '{json_text}'")
def step_load_yaml_text_valid_json(context: Context, json_text: str) -> None:
    context.yaml_result = ActorConfiguration.load_yaml_text(json_text)


@then('the load_yaml_text result should have key "{key}" equal to "{value}"')
def step_assert_yaml_result_key(context: Context, key: str, value: str) -> None:
    assert isinstance(context.yaml_result, dict), (
        f"Expected dict, got {type(context.yaml_result)}"
    )
    assert context.yaml_result.get(key) == value, (
        f"Expected result['{key}'] == '{value}', got {context.yaml_result.get(key)!r}"
    )


# ── load_yaml_text: YAML fallback ───────────────────────────────────


@when('I call load_yaml_text with YAML text "{yaml_text}"')
def step_load_yaml_text_yaml_fallback(context: Context, yaml_text: str) -> None:
    # Behave passes escaped \\n as literal; convert to real newlines
    real_text = yaml_text.replace("\\n", "\n")
    context.yaml_result = ActorConfiguration.load_yaml_text(real_text)


# ── _load_v2_yaml_content: interpolation returns non-dict (line 127) ─


@given("_interpolate_env_vars is patched to return a list")
def step_patch_interpolate(context: Context) -> None:
    global _active_patch
    _active_patch = patch.object(
        ActorConfiguration,
        "_interpolate_env_vars",
        staticmethod(lambda config: ["not", "a", "dict"]),
    )
    _active_patch.start()


@when("I call _load_v2_yaml_content with valid YAML via patched interpolation")
def step_call_load_v2_patched(context: Context) -> None:
    global _active_patch
    context.caught_error = None
    try:
        ActorConfiguration._load_v2_yaml_content("key: value\n")
    except ValueError as exc:
        context.caught_error = exc
    finally:
        # Always clean up the patch
        if _active_patch is not None:
            _active_patch.stop()
            _active_patch = None
