"""Helper script for tdd_actor_list_validation.robot smoke tests.

Each subcommand exercises the real ``ActorRegistry._actor_name()`` code path
with a provider whose default model contains ``/`` characters to reproduce
bug #592.  The helper reports the **real** outcome: it exits 0 and prints
the sentinel when the operation succeeds (bug is fixed), and exits 1 when
the bug is still present.  The ``tdd_expected_fail_listener`` on the Robot
side handles pass/fail inversion while the bug remains open.
"""

from __future__ import annotations

import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any
from unittest.mock import patch

# Ensure local source tree is importable
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

# Ensure the features directory is importable for shared mocks
_FEATURES = str(Path(__file__).resolve().parents[1] / "features")
if _FEATURES not in sys.path:
    sys.path.insert(0, _FEATURES)

from mocks.fake_provider import FakeProviderInfo, make_registry  # noqa: E402
from typer.testing import CliRunner  # noqa: E402

from cleveragents.cli.commands.actor import app as actor_app  # noqa: E402

runner = CliRunner()

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

_MULTI_SLASH_PROVIDERS = [
    FakeProviderInfo(
        name="Openrouter",
        default_model="anthropic/claude-sonnet-4-20250514",
    ),
]


# ---------------------------------------------------------------------------
# Subcommands
# ---------------------------------------------------------------------------


def list_validation_error() -> None:
    """Invoke ``actor list`` with a multi-slash model provider.

    Exits 0 with sentinel when the command succeeds with valid names (bug
    fixed).  Exits 1 when the command fails or produces bad names (bug
    still present).

    Built-in actors are populated via ``ensure_built_in_actors()``
    **before** the list call because ``list_actors()`` is now read-only
    and no longer triggers writes (bug #797 fix).
    """
    mock_service, registry = make_registry(_MULTI_SLASH_PROVIDERS)
    registry.ensure_built_in_actors()
    assert mock_service.upsert_actor.call_count > 0, (
        "upsert_actor was never called — ensure_built_in_actors may not have run"
    )
    with patch(
        _PATCH_GET_SERVICES,
        return_value=(mock_service, registry),
    ):
        result = runner.invoke(actor_app, ["list"])
    if result.exit_code != 0:
        # Bug present — command failed.
        print(
            f"actor list failed with exit code {result.exit_code}",
            file=sys.stderr,
        )
        sys.exit(1)
    # Command succeeded — check if names are valid (single slash).
    for call in mock_service.upsert_actor.call_args_list:
        name: Any = call.kwargs.get("name", "")
        if name.count("/") != 1:
            print(
                f"actor list produced bad name {name!r} with {name.count('/')} slashes",
                file=sys.stderr,
            )
            sys.exit(1)
    # Bug fixed — command succeeded with valid names.
    print("tdd-actor-list-validation-error-ok")


def name_slash_check() -> None:
    """Verify that upserted actor names have exactly one ``/``.

    Exits 0 with sentinel when all names have exactly one slash (bug
    fixed).  Exits 1 when any name has 0 or 2+ slashes (bug still
    present).

    Built-in actors are populated via ``ensure_built_in_actors()``
    directly because ``list_actors()`` is now read-only and no longer
    triggers writes (bug #797 fix).
    """
    mock_service, registry = make_registry(_MULTI_SLASH_PROVIDERS)
    registry.ensure_built_in_actors()

    assert mock_service.upsert_actor.call_count > 0, (
        "upsert_actor was never called — ensure_built_in_actors may not have run"
    )
    for call in mock_service.upsert_actor.call_args_list:
        name: Any = call.kwargs.get("name", "")
        if name.count("/") != 1:
            # Bug present — name construction produces bad names.
            print(
                f"actor name {name!r} has {name.count('/')} slashes",
                file=sys.stderr,
            )
            sys.exit(1)
    # Bug fixed — all names have exactly one slash.
    print("tdd-actor-name-slash-check-ok")


def list_json_validation() -> None:
    """Invoke ``actor list --format json`` with a multi-slash model provider.

    Exits 0 with sentinel when the command succeeds with valid names (bug
    fixed).  Exits 1 when the command fails or produces bad names (bug
    still present).

    Built-in actors are populated via ``ensure_built_in_actors()``
    **before** the list call because ``list_actors()`` is now read-only
    and no longer triggers writes (bug #797 fix).
    """
    mock_service, registry = make_registry(_MULTI_SLASH_PROVIDERS)
    registry.ensure_built_in_actors()
    assert mock_service.upsert_actor.call_count > 0, (
        "upsert_actor was never called — ensure_built_in_actors may not have run"
    )
    with patch(
        _PATCH_GET_SERVICES,
        return_value=(mock_service, registry),
    ):
        result = runner.invoke(actor_app, ["list", "--format", "json"])
    if result.exit_code != 0:
        # Bug present — command failed.
        print(
            f"actor list --format json failed with exit code {result.exit_code}",
            file=sys.stderr,
        )
        sys.exit(1)
    # Command succeeded — check if names are valid (single slash).
    for call in mock_service.upsert_actor.call_args_list:
        name: Any = call.kwargs.get("name", "")
        if name.count("/") != 1:
            print(
                f"actor list --format json produced bad name {name!r} with "
                f"{name.count('/')} slashes",
                file=sys.stderr,
            )
            sys.exit(1)
    # Bug fixed — command succeeded with valid names.
    print("tdd-actor-list-json-validation-ok")


# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------

_COMMANDS: dict[str, Callable[[], None]] = {
    "list-validation-error": list_validation_error,
    "name-slash-check": name_slash_check,
    "list-json-validation": list_json_validation,
}

if __name__ == "__main__":
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>", file=sys.stderr)
        sys.exit(1)
    cmd = _COMMANDS[sys.argv[1]]
    cmd()
