"""Step definitions for checkpoint_auto_triggers.feature — ToolRunner steps.

Covers automatic checkpoint creation for the ToolRunner triggers:
  - before_tool_execute
  - after_tool_execute

Also covers the config service registration step.
"""

from __future__ import annotations

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

from cleveragents.application.services.checkpoint_service import CheckpointService
from cleveragents.tool.registry import ToolRegistry
from cleveragents.tool.runner import DEFAULT_AUTO_TRIGGERS, ToolRunner
from cleveragents.tool.runtime import ToolSpec

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


def _make_write_tool_spec(name: str) -> ToolSpec:
    """Create a ToolSpec with writes=True."""
    from cleveragents.domain.models.core.tool import ToolCapability

    return ToolSpec(
        name=name,
        description="A write tool for testing",
        capabilities=ToolCapability(writes=True, read_only=False),
        handler=lambda inputs: {"written": True},
    )


def _make_read_tool_spec(name: str) -> ToolSpec:
    """Create a ToolSpec with writes=False (read-only)."""
    from cleveragents.domain.models.core.tool import ToolCapability

    return ToolSpec(
        name=name,
        description="A read-only tool for testing",
        capabilities=ToolCapability(writes=False, read_only=True),
        handler=lambda inputs: {"data": "read"},
    )


def _build_tool_runner(context: Context) -> ToolRunner:
    """Build the ToolRunner, applying any disabled triggers."""
    active_triggers = DEFAULT_AUTO_TRIGGERS - getattr(
        context, "disabled_triggers", set()
    )
    return ToolRunner(
        registry=context.registry,
        checkpoint_service=context.checkpoint_service,
        auto_checkpoint_triggers=frozenset(active_triggers),
    )


# ---------------------------------------------------------------------------
# Given steps — ToolRunner
# ---------------------------------------------------------------------------


@given('a tool runner with a checkpoint service and plan_id "{plan_id}"')
def step_tool_runner_with_checkpoint(context: Context, plan_id: str) -> None:
    context.checkpoint_service = CheckpointService()
    context.registry = ToolRegistry()
    context.disabled_triggers: set[str] = set()
    context.plan_id = plan_id
    context.tool_runner = None  # built lazily after trigger config


@given("a tool runner WITHOUT a checkpoint service")
def step_tool_runner_without_checkpoint(context: Context) -> None:
    context.checkpoint_service = None
    context.registry = ToolRegistry()
    context.plan_id = ""
    context.disabled_triggers = set()
    context.tool_runner = None


@given('a write tool "{tool_name}" is registered')
def step_register_write_tool(context: Context, tool_name: str) -> None:
    spec = _make_write_tool_spec(tool_name)
    context.registry.register(spec)
    context.tool_name = tool_name


@given('a read-only tool "{tool_name}" is registered')
def step_register_read_tool(context: Context, tool_name: str) -> None:
    spec = _make_read_tool_spec(tool_name)
    context.registry.register(spec)
    context.tool_name = tool_name


@given('the "{trigger}" trigger is disabled')
def step_disable_trigger(context: Context, trigger: str) -> None:
    if not hasattr(context, "disabled_triggers"):
        context.disabled_triggers = set()
    context.disabled_triggers.add(trigger)


# ---------------------------------------------------------------------------
# When steps — ToolRunner
# ---------------------------------------------------------------------------


@when('I execute tool "{tool_name}" with plan_id "{plan_id}"')
def step_execute_tool_with_plan_id(
    context: Context, tool_name: str, plan_id: str
) -> None:
    runner = _build_tool_runner(context)
    context.tool_result = runner.execute(
        tool_name,
        {},
        plan_id=plan_id,
    )
    context.exception = None


@when('I execute tool "{tool_name}" without a plan_id')
def step_execute_tool_without_plan_id(context: Context, tool_name: str) -> None:
    runner = _build_tool_runner(context)
    try:
        context.tool_result = runner.execute(tool_name, {})
        context.exception = None
    except Exception as exc:
        context.exception = exc


# ---------------------------------------------------------------------------
# Then steps — ToolRunner checkpoints
# ---------------------------------------------------------------------------


@then('a checkpoint with phase "{phase}" should exist for plan "{plan_id}"')
def step_checkpoint_phase_exists(context: Context, phase: str, plan_id: str) -> None:
    checkpoints = context.checkpoint_service.list_checkpoints(plan_id)
    reasons = [cp.metadata.reason for cp in checkpoints]
    # Phase is stored in metadata.phase; trigger name is in metadata.reason
    assert any(phase in r for r in reasons), (
        f"Expected a checkpoint with '{phase}' in reason for plan {plan_id}. "
        f"Got reasons: {reasons}"
    )


@then('no checkpoint with phase "{phase}" should exist for plan "{plan_id}"')
def step_no_checkpoint_phase(context: Context, phase: str, plan_id: str) -> None:
    checkpoints = context.checkpoint_service.list_checkpoints(plan_id)
    reasons = [cp.metadata.reason for cp in checkpoints]
    assert not any(phase in r for r in reasons), (
        f"Expected NO checkpoint with '{phase}' in reason for plan {plan_id}. "
        f"Got reasons: {reasons}"
    )


@then('{count:d} checkpoints should exist for plan "{plan_id}"')
def step_checkpoint_count(context: Context, count: int, plan_id: str) -> None:
    checkpoints = context.checkpoint_service.list_checkpoints(plan_id)
    assert len(checkpoints) == count, (
        f"Expected {count} checkpoints for plan {plan_id}, got {len(checkpoints)}"
    )


@then("no auto-trigger exception should be raised")
def step_no_exception(context: Context) -> None:
    assert getattr(context, "exception", None) is None, (
        f"Unexpected exception: {context.exception}"
    )


# ---------------------------------------------------------------------------
# Config service steps
# ---------------------------------------------------------------------------


@given("the config service is initialized")
def step_config_service_initialized(context: Context) -> None:
    from cleveragents.application.services.config_service import ConfigService

    context.config_service = ConfigService()


@when('I get the config key "{key}"')
def step_get_config_key(context: Context, key: str) -> None:
    from cleveragents.application.services.config_service import ConfigService

    entry = ConfigService.get_entry(key)
    context.config_value = entry.default if entry is not None else None


@then('the value should contain "{substring}"')
def step_value_contains(context: Context, substring: str) -> None:
    value = context.config_value
    assert value is not None, "Config value for key is None"
    assert substring in str(value), f"Expected '{substring}' in config value '{value}'"
