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

Each subcommand exercises the real ``ActorRegistry.list_actors()`` code
path to reproduce bug #797.  The ``actor list`` command is invoked via
:class:`typer.testing.CliRunner` with a mock actor service and fake
provider registry, and the helper checks whether ``upsert_actor()`` or
``set_default_actor()`` was called (database writes).

The helper reports the **real** outcome: it exits 0 and prints the
sentinel when the bug is fixed (no writes), and exits 1 when the bug
is still present (writes detected).  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 NoReturn
from unittest.mock import patch

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

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 = CliRunner()

_PATCH_TARGET: str = "cleveragents.cli.commands.actor._get_services"

_DEFAULT_PROVIDERS: list[FakeProviderInfo] = [
    FakeProviderInfo(
        name="FakeProvider",
        default_model="fake-org/fake-model-v1",
    ),
]


def _fail(message: str) -> NoReturn:
    """Print an error message to stderr and exit with code 1."""
    print(message, file=sys.stderr)
    sys.exit(1)


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


def check_no_upsert() -> None:
    """Verify that ``actor list`` does not call ``upsert_actor``.

    Exits 0 with sentinel when ``upsert_actor`` was NOT called (bug
    fixed).  Exits 1 when ``upsert_actor`` WAS called (bug still
    present).
    """
    mock_service, registry = make_registry(_DEFAULT_PROVIDERS)
    with patch(
        _PATCH_TARGET,
        return_value=(mock_service, registry),
    ):
        runner.invoke(actor_app, ["list"])

    call_count: int = mock_service.upsert_actor.call_count
    if call_count > 0:
        _fail(
            f"upsert_actor was called {call_count} time(s) during "
            f"'actor list'. A read-only list command must not trigger "
            f"database writes (bug #797)."
        )
    print("tdd-actor-list-no-db-update-no-upsert-ok")


def check_no_set_default() -> None:
    """Verify that ``actor list`` does not call ``set_default_actor``.

    Exits 0 with sentinel when ``set_default_actor`` was NOT called
    (bug fixed).  Exits 1 when ``set_default_actor`` WAS called (bug
    still present).
    """
    mock_service, registry = make_registry(_DEFAULT_PROVIDERS)
    with patch(
        _PATCH_TARGET,
        return_value=(mock_service, registry),
    ):
        runner.invoke(actor_app, ["list"])

    call_count: int = mock_service.set_default_actor.call_count
    if call_count > 0:
        _fail(
            f"set_default_actor was called {call_count} time(s) during "
            f"'actor list'. A read-only list command must not trigger "
            f"database writes (bug #797)."
        )
    print("tdd-actor-list-no-db-update-no-set-default-ok")


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

_COMMANDS: dict[str, Callable[[], None]] = {
    "check-no-upsert": check_no_upsert,
    "check-no-set-default": check_no_set_default,
}

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: Callable[[], None] = _COMMANDS[sys.argv[1]]
    cmd()
