"""Step definitions for TDD Issue #592 — actor list validation error.

These steps exercise the *real* ``ActorRegistry._actor_name()`` code path
with a provider whose default model name contains ``/`` characters.

Root cause (fixed)
~~~~~~~~~~~~~~~~~~
``ActorRegistry._actor_name()`` builds names via
``f"{provider}/{model}"``.  For providers whose default model already
contains ``/`` (e.g. OpenRouter's ``anthropic/claude-sonnet-4-20250514``),
the result has 2+ slashes.  The fix sanitises by replacing ``/`` with ``-``
and lowercasing the result.

After the virtual built-in refactor (issue #10923), built-in actors are
resolved in-memory from the provider registry (never persisted).  The name
sanitisation now happens in the virtual resolution path rather than via
``ensure_built_in_actors`` + ``upsert_actor``.
"""

from __future__ import annotations

import json
from typing import Any
from unittest.mock import patch

from behave import given, then, when
from typer.testing import CliRunner

from cleveragents.cli.commands.actor import app as actor_app
from features.mocks.fake_provider import FakeProviderInfo, make_registry

runner = CliRunner()

_PATCH_GET_SERVICES = "cleveragents.cli.commands.actor._get_services"


# ---------------------------------------------------------------------------
# Given steps
# ---------------------------------------------------------------------------


@given("an actor registry with a multi-slash model provider for tdd-actor-validation")
def step_multi_slash_provider(context: Any) -> None:
    """Set up a real ``ActorRegistry`` with a multi-slash model provider.

    The provider name is ``Openrouter`` and the default model is
    ``anthropic/claude-sonnet-4-20250514`` — reproducing the exact
    combination that triggered bug #592.
    """
    fake_provider = FakeProviderInfo(
        name="Openrouter",
        default_model="anthropic/claude-sonnet-4-20250514",
    )
    service, registry = make_registry([fake_provider])
    context.tdd_actor_service = service
    context.tdd_actor_registry = registry


# ---------------------------------------------------------------------------
# When steps
# ---------------------------------------------------------------------------


@when("I run actor list via the tdd-actor-validation CLI")
def step_run_actor_list(context: Any) -> None:
    """Invoke ``actor list`` with the prepared registry.

    After the virtual built-in refactor, ``list_actors()`` is entirely
    read-only — virtual built-in actors are resolved in-memory without
    any DB writes.
    """
    with patch(
        _PATCH_GET_SERVICES,
        return_value=(
            context.tdd_actor_service,
            context.tdd_actor_registry,
        ),
    ):
        context.tdd_actor_result = runner.invoke(actor_app, ["list"])


@when("I run actor list with format json via the tdd-actor-validation CLI")
def step_run_actor_list_json(context: Any) -> None:
    """Invoke ``actor list --format json`` with the prepared registry."""
    with patch(
        _PATCH_GET_SERVICES,
        return_value=(
            context.tdd_actor_service,
            context.tdd_actor_registry,
        ),
    ):
        context.tdd_actor_result = runner.invoke(
            actor_app, ["list", "--format", "json"]
        )


# ---------------------------------------------------------------------------
# Then steps
# ---------------------------------------------------------------------------


@then("the tdd-actor-validation exit code should be {code:d}")
def step_exit_code(context: Any, code: int) -> None:
    result = context.tdd_actor_result
    assert result is not None, "actor list was not invoked"
    actual = result.exit_code
    assert actual == code, (
        f"Expected exit code {code}, got {actual}. Output:\n{result.output}"
    )


@then('the tdd-actor-validation output should not contain "{text}"')
def step_output_not_contains(context: Any, text: str) -> None:
    result = context.tdd_actor_result
    assert result is not None, "actor list was not invoked"
    output = result.output
    assert text.lower() not in output.lower(), (
        f"Did NOT expect '{text}' in output but found it:\n{output}"
    )


@then("the tdd-actor-validation upserted actor name should contain exactly one slash")
def step_upserted_name_single_slash(context: Any) -> None:
    """Verify that virtual built-in actor names have exactly one ``/``.

    After the virtual built-in refactor, names are produced through the
    virtual resolution path (``_resolve_virtual_builtin_actors``).  We
    check the actors returned by ``list_actors()`` directly instead of
    inspecting ``upsert_actor`` call arguments.
    """
    actors = context.tdd_actor_registry.list_actors()
    assert len(actors) > 0, (
        "list_actors() returned no actors — expected virtual built-ins"
    )
    for actor in actors:
        slash_count = actor.name.count("/")
        assert slash_count == 1, (
            f"Expected exactly 1 slash in actor name '{actor.name}', "
            f"found {slash_count}"
        )


@then("the tdd-actor-validation output should be valid JSON")
def step_output_valid_json(context: Any) -> None:
    result = context.tdd_actor_result
    assert result is not None, "actor list was not invoked"
    try:
        json.loads(result.output)
    except json.JSONDecodeError as exc:
        raise AssertionError(f"Output is not valid JSON:\n{result.output}") from exc
