"""Step definitions for ToolLifecycle execute hook tests (issue #2820)."""

from typing import Any

from behave import then, when
from behave.runner import Context

from cleveragents.domain.models.core.tool import (
    Tool,
    ToolLifecycle,
    ToolSource,
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _make_builtin_tool(**overrides: Any) -> Tool:
    """Create a minimal builtin Tool with optional overrides."""
    defaults: dict[str, Any] = {
        "name": "local/test-tool",
        "description": "A test tool",
        "source": ToolSource.BUILTIN,
    }
    defaults.update(overrides)
    return Tool(**defaults)


# ---------------------------------------------------------------------------
# When steps — ToolLifecycle construction
# ---------------------------------------------------------------------------


@when('I create a tool lifecycle with execute hook "{execute}"')
def step_create_lifecycle_with_execute(context: Context, execute: str) -> None:
    """Create a ToolLifecycle with only the execute hook set."""
    context.tool_lifecycle = ToolLifecycle(execute=execute)
    context.tool_model_error = None


@when("I create a tool lifecycle with all four hooks set")
def step_create_lifecycle_all_four(context: Context) -> None:
    """Create a ToolLifecycle with all four lifecycle hooks set."""
    context.tool_lifecycle = ToolLifecycle(
        discover="hooks.discover",
        activate="hooks.activate",
        execute="hooks.execute",
        deactivate="hooks.deactivate",
    )
    context.tool_model_error = None


@when("I create a tool lifecycle with no hooks")
def step_create_lifecycle_no_hooks(context: Context) -> None:
    """Create a ToolLifecycle with no hooks (all default to None)."""
    context.tool_lifecycle = ToolLifecycle()
    context.tool_model_error = None


@when('I create a tool lifecycle with only execute hook "{execute}"')
def step_create_lifecycle_only_execute(context: Context, execute: str) -> None:
    """Create a ToolLifecycle with only the execute hook set (all others None)."""
    context.tool_lifecycle = ToolLifecycle(execute=execute)
    context.tool_model_error = None


# ---------------------------------------------------------------------------
# When steps — Tool.from_config() with lifecycle
# ---------------------------------------------------------------------------


@when('I load a tool from config with lifecycle execute "{execute}"')
def step_load_tool_from_config_with_lifecycle_execute(
    context: Context, execute: str
) -> None:
    """Load a Tool from config dict that includes a lifecycle.execute hook."""
    config: dict[str, Any] = {
        "name": "local/lifecycle-tool",
        "description": "Tool with lifecycle execute hook",
        "source": "builtin",
        "lifecycle": {
            "execute": execute,
        },
    }
    context.tool_model = Tool.from_config(config)
    context.tool_model_error = None


@when("I load a tool from config with lifecycle execute None")
def step_load_tool_from_config_with_lifecycle_execute_none(context: Context) -> None:
    """Load a Tool from config dict that includes a lifecycle with execute=None."""
    config: dict[str, Any] = {
        "name": "local/lifecycle-tool-none",
        "description": "Tool with lifecycle execute=None",
        "source": "builtin",
        "lifecycle": {
            "discover": "hooks.discover",
        },
    }
    context.tool_model = Tool.from_config(config)
    context.tool_model_error = None


# ---------------------------------------------------------------------------
# When steps — Tool.as_cli_dict() with lifecycle
# ---------------------------------------------------------------------------


@when('I create a tool with lifecycle execute "{execute}" and call as_cli_dict')
def step_create_tool_with_lifecycle_execute_and_cli_dict(
    context: Context, execute: str
) -> None:
    """Create a Tool with a lifecycle.execute hook and call as_cli_dict."""
    tool = _make_builtin_tool(
        lifecycle=ToolLifecycle(execute=execute),
    )
    context.tool_cli_dict = tool.as_cli_dict()
    context.tool_model_error = None


@when("I create a tool with no lifecycle and call as_cli_dict")
def step_create_tool_no_lifecycle_and_cli_dict(context: Context) -> None:
    """Create a Tool with no lifecycle and call as_cli_dict."""
    tool = _make_builtin_tool()
    context.tool_cli_dict = tool.as_cli_dict()
    context.tool_model_error = None


# ---------------------------------------------------------------------------
# Then steps — ToolLifecycle field assertions
# ---------------------------------------------------------------------------


@then('the tool lifecycle execute should be "{expected}"')
def step_check_lifecycle_execute(context: Context, expected: str) -> None:
    """Assert the lifecycle execute hook equals the expected value."""
    # Support both context.tool_lifecycle and context.tool_model.lifecycle
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.execute
    else:
        assert context.tool_model.lifecycle is not None, (
            "Expected tool_model.lifecycle to be set, but it is None"
        )
        actual = context.tool_model.lifecycle.execute
    assert actual == expected, (
        f"Expected lifecycle.execute == {expected!r}, got {actual!r}"
    )


@then("the tool lifecycle execute should be None")
def step_check_lifecycle_execute_none(context: Context) -> None:
    """Assert the lifecycle execute hook is None."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.execute
    elif context.tool_model.lifecycle is not None:
        actual = context.tool_model.lifecycle.execute
    else:
        actual = None
    assert actual is None, f"Expected lifecycle.execute to be None, got {actual!r}"


@then("the tool lifecycle discover should be None")
def step_check_lifecycle_discover_none(context: Context) -> None:
    """Assert the lifecycle discover hook is None."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.discover
    elif context.tool_model.lifecycle is not None:
        actual = context.tool_model.lifecycle.discover
    else:
        actual = None
    assert actual is None, f"Expected lifecycle.discover to be None, got {actual!r}"


@then('the tool lifecycle activate should be "{expected}"')
def step_check_lifecycle_activate(context: Context, expected: str) -> None:
    """Assert the lifecycle activate hook equals the expected value."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.activate
    else:
        assert context.tool_model.lifecycle is not None
        actual = context.tool_model.lifecycle.activate
    assert actual == expected, (
        f"Expected lifecycle.activate == {expected!r}, got {actual!r}"
    )


@then("the tool lifecycle activate should be None")
def step_check_lifecycle_activate_none(context: Context) -> None:
    """Assert the lifecycle activate hook is None."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.activate
    elif context.tool_model.lifecycle is not None:
        actual = context.tool_model.lifecycle.activate
    else:
        actual = None
    assert actual is None, f"Expected lifecycle.activate to be None, got {actual!r}"


@then('the tool lifecycle deactivate should be "{expected}"')
def step_check_lifecycle_deactivate(context: Context, expected: str) -> None:
    """Assert the lifecycle deactivate hook equals the expected value."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.deactivate
    else:
        assert context.tool_model.lifecycle is not None
        actual = context.tool_model.lifecycle.deactivate
    assert actual == expected, (
        f"Expected lifecycle.deactivate == {expected!r}, got {actual!r}"
    )


@then("the tool lifecycle deactivate should be None")
def step_check_lifecycle_deactivate_none(context: Context) -> None:
    """Assert the lifecycle deactivate hook is None."""
    if hasattr(context, "tool_lifecycle"):
        actual = context.tool_lifecycle.deactivate
    elif context.tool_model.lifecycle is not None:
        actual = context.tool_model.lifecycle.deactivate
    else:
        actual = None
    assert actual is None, f"Expected lifecycle.deactivate to be None, got {actual!r}"


# ---------------------------------------------------------------------------
# Then steps — as_cli_dict lifecycle assertions
# ---------------------------------------------------------------------------


@then('the tool cli dict lifecycle should have key "{key}"')
def step_check_cli_dict_lifecycle_has_key(context: Context, key: str) -> None:
    """Assert the lifecycle dict in as_cli_dict contains the given key."""
    assert "lifecycle" in context.tool_cli_dict, (
        "Expected 'lifecycle' key in cli dict, but it was absent"
    )
    lc = context.tool_cli_dict["lifecycle"]
    assert key in lc, (
        f"Expected lifecycle dict to have key {key!r}, got keys: {list(lc.keys())}"
    )


@then('the tool cli dict lifecycle execute should be "{expected}"')
def step_check_cli_dict_lifecycle_execute(context: Context, expected: str) -> None:
    """Assert the lifecycle.execute value in as_cli_dict equals expected."""
    assert "lifecycle" in context.tool_cli_dict, (
        "Expected 'lifecycle' key in cli dict, but it was absent"
    )
    actual = context.tool_cli_dict["lifecycle"].get("execute")
    assert actual == expected, (
        f"Expected lifecycle.execute == {expected!r}, got {actual!r}"
    )


@then('the tool cli dict should not have key "{key}"')
def step_check_cli_dict_missing_key(context: Context, key: str) -> None:
    """Assert the cli dict does NOT contain the given key."""
    assert key not in context.tool_cli_dict, (
        f"Expected cli dict NOT to have key {key!r}, but it was present"
    )
