"""Behave step definitions for PluginState.EXECUTING lifecycle state.

Covers the execute_plugin() method on PluginManager, which transitions
plugins through ACTIVATED -> EXECUTING -> ACTIVATED (success) or
ACTIVATED -> EXECUTING -> ERRORED (failure).

Based on issue #5691.
"""

from __future__ import annotations

from unittest.mock import patch

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

from cleveragents.infrastructure.plugins.exceptions import (
    PluginError,
    PluginNotFoundError,
)
from cleveragents.infrastructure.plugins.types import PluginState

# ---------------------------------------------------------------------------
# Execute plugin — happy path
# ---------------------------------------------------------------------------


@when('I execute method "search" on plugin "{name}" with valid args')
def step_execute_plugin_search(context: Context, name: str) -> None:
    context.caught_exception = None
    context.execution_result = None
    try:
        context.execution_result = context.manager.execute_plugin(
            name,
            "search",
            "test query",
            scope=frozenset(),
            max_results=5,
        )
    except PluginError as exc:
        context.caught_exception = exc


@then("the execution result should be an empty list")
def step_execution_result_empty_list(context: Context) -> None:
    assert context.execution_result == [], (
        f"Expected empty list, got {context.execution_result!r}"
    )


# ---------------------------------------------------------------------------
# Execute plugin — error path
# ---------------------------------------------------------------------------


@when('I attempt to execute a failing method on plugin "{name}"')
def step_attempt_execute_failing(context: Context, name: str) -> None:
    context.caught_exception = None
    instance = context.manager.get_plugin_instance(name)
    if instance is not None:
        with patch.object(instance, "search", side_effect=RuntimeError("boom")):
            try:
                context.manager.execute_plugin(
                    name,
                    "search",
                    "test query",
                    scope=frozenset(),
                    max_results=5,
                )
            except PluginError as exc:
                context.caught_exception = exc
    else:
        try:
            context.manager.execute_plugin(name, "search", "test", scope=frozenset())
        except PluginError as exc:
            context.caught_exception = exc


# ---------------------------------------------------------------------------
# Execute plugin — guard: not activated
# ---------------------------------------------------------------------------


@when('I attempt to execute method on non-activated plugin "{name}"')
def step_attempt_execute_non_activated(context: Context, name: str) -> None:
    context.caught_exception = None
    try:
        context.manager.execute_plugin(
            name,
            "search",
            "test query",
            scope=frozenset(),
            max_results=5,
        )
    except PluginError as exc:
        context.caught_exception = exc


@when('I attempt to execute method on unknown plugin "{name}"')
def step_attempt_execute_unknown(context: Context, name: str) -> None:
    context.caught_exception = None
    try:
        context.manager.execute_plugin(
            name,
            "search",
            "test query",
            scope=frozenset(),
            max_results=5,
        )
    except (PluginError, PluginNotFoundError) as exc:
        context.caught_exception = exc


# ---------------------------------------------------------------------------
# Thread safety: EXECUTING state prevents deactivation
# ---------------------------------------------------------------------------


@when('I manually set plugin "{name}" state to "{state}"')
def step_manually_set_state(context: Context, name: str, state: str) -> None:
    descriptor = context.manager.get_plugin(name)
    descriptor.state = PluginState(state)
