"""Helper script for actor_run_signature.robot integration tests.

Tests that the ``actor run`` and ``actor-run`` CLI commands accept
positional NAME and PROMPT arguments while preserving --config fallback.
"""

from __future__ import annotations

import os
import sys
import tempfile
from pathlib import Path
from types import SimpleNamespace
from typing import Any
from unittest.mock import AsyncMock, MagicMock, 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)

import typer  # noqa: E402
from typer.testing import CliRunner  # noqa: E402

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

runner = CliRunner()

_SIMPLE_YAML = """\
name: smoke-actor
type: custom
provider: openai
model: gpt-4
tools:
  - operation: identity
"""


def _write_yaml(content: str) -> str:
    fd, path = tempfile.mkstemp(suffix=".yaml")
    with os.fdopen(fd, "w", encoding="utf-8") as fh:
        fh.write(content)
    return path


def _make_app(*, result: str) -> MagicMock:
    app_exec = MagicMock()
    app_exec.config = SimpleNamespace(global_context={})
    app_exec.run_single_shot = AsyncMock(return_value=result)
    return app_exec


def positional_with_config() -> None:
    """Test positional NAME + PROMPT with --config fallback."""
    path = _write_yaml(_SIMPLE_YAML)
    app_exec = _make_app(result="config response")

    try:
        with patch(
            "cleveragents.cli.commands.actor.ReactiveCleverAgentsApp",
            return_value=app_exec,
        ):
            result = runner.invoke(
                actor_app,
                ["run", "--config", path, "my-actor", "hello world"],
            )
        if result.exit_code != 0:
            print(
                f"FAIL: expected exit code 0, got {result.exit_code}",
                file=sys.stderr,
            )
            print(result.output, file=sys.stderr)
            sys.exit(1)
        if "config response" not in result.output:
            print(
                f"FAIL: expected 'config response' in output, got: {result.output}",
                file=sys.stderr,
            )
            sys.exit(1)
        print("positional-with-config-ok")
    finally:
        Path(path).unlink(missing_ok=True)


def positional_from_registry() -> None:
    """Test positional NAME resolved from the actor registry."""
    path = _write_yaml(_SIMPLE_YAML)
    app_exec = _make_app(result="registry response")

    def _mock_resolve(name: str, config: list[Any]) -> list[Any]:
        return [Path(path)]

    try:
        with (
            patch(
                "cleveragents.cli.commands.actor_run._resolve_config_files",
                side_effect=_mock_resolve,
            ),
            patch(
                "cleveragents.cli.commands.actor_run.ReactiveCleverAgentsApp",
                return_value=app_exec,
            ),
        ):
            result = runner.invoke(
                actor_run_app,
                ["local/my-actor", "hello from registry"],
            )

        if result.exit_code != 0:
            print(
                f"FAIL: expected exit code 0, got {result.exit_code}",
                file=sys.stderr,
            )
            print(result.output, file=sys.stderr)
            sys.exit(1)
        if "registry response" not in result.output:
            print(
                f"FAIL: expected 'registry response' in output, got: {result.output}",
                file=sys.stderr,
            )
            sys.exit(1)
        print("positional-from-registry-ok")
    finally:
        Path(path).unlink(missing_ok=True)


def actor_app_registry_resolution() -> None:
    """Test positional NAME resolved from the actor registry via actor_app."""
    path = _write_yaml(_SIMPLE_YAML)
    app_exec = _make_app(result="actor-app registry response")

    def _mock_resolve(name: str, config: list[Any]) -> list[Any]:
        return [Path(path)]

    try:
        with (
            patch(
                "cleveragents.cli.commands.actor._resolve_config_files",
                side_effect=_mock_resolve,
            ),
            patch(
                "cleveragents.cli.commands.actor.ReactiveCleverAgentsApp",
                return_value=app_exec,
            ),
        ):
            result = runner.invoke(
                actor_app,
                ["run", "local/my-actor", "hello from actor_app registry"],
            )

        if result.exit_code != 0:
            print(
                f"FAIL: expected exit code 0, got {result.exit_code}",
                file=sys.stderr,
            )
            print(result.output, file=sys.stderr)
            sys.exit(1)
        if "actor-app registry response" not in result.output:
            print(
                "FAIL: expected 'actor-app registry response' "
                f"in output, got: {result.output}",
                file=sys.stderr,
            )
            sys.exit(1)
        print("actor-app-registry-ok")
    finally:
        Path(path).unlink(missing_ok=True)


def unknown_actor_name() -> None:
    """Test error when actor name is not found in registry."""

    def _not_found_resolve(name: str, config: list[Any]) -> list[Any]:
        typer.echo(
            f"Error: Actor '{name}' not found in registry and no --config provided.",
            err=True,
        )
        raise typer.Exit(code=2)

    with patch(
        "cleveragents.cli.commands.actor_run._resolve_config_files",
        side_effect=_not_found_resolve,
    ):
        result = runner.invoke(
            actor_run_app,
            ["nonexistent/actor", "test prompt"],
        )

    if result.exit_code != 2:
        print(
            f"FAIL: expected exit code 2, got {result.exit_code}",
            file=sys.stderr,
        )
        print(result.output, file=sys.stderr)
        sys.exit(1)
    combined = (result.output or "") + (getattr(result, "stderr", None) or "")
    if "not found in registry" not in combined:
        print(
            "FAIL: expected 'not found in registry' in output",
            file=sys.stderr,
        )
        print(combined, file=sys.stderr)
        sys.exit(1)
    print("unknown-actor-name-ok")


def actor_app_unknown_name() -> None:
    """Test error when actor name is not found in registry via actor_app."""

    def _not_found_resolve(name: str, config: list[Any]) -> list[Any]:
        typer.echo(
            f"Error: Actor '{name}' not found in registry and no --config provided.",
            err=True,
        )
        raise typer.Exit(code=2)

    with patch(
        "cleveragents.cli.commands.actor._resolve_config_files",
        side_effect=_not_found_resolve,
    ):
        result = runner.invoke(
            actor_app,
            ["run", "nonexistent/actor", "test prompt"],
        )

    if result.exit_code != 2:
        print(
            f"FAIL: expected exit code 2, got {result.exit_code}",
            file=sys.stderr,
        )
        print(result.output, file=sys.stderr)
        sys.exit(1)
    combined = (result.output or "") + (getattr(result, "stderr", None) or "")
    if "not found in registry" not in combined:
        print(
            "FAIL: expected 'not found in registry' in output",
            file=sys.stderr,
        )
        print(combined, file=sys.stderr)
        sys.exit(1)
    print("actor-app-unknown-name-ok")


# ---------------------------------------------------------------------------
# Main dispatcher
# ---------------------------------------------------------------------------

_COMMANDS: dict[str, Any] = {
    "positional-with-config": positional_with_config,
    "positional-from-registry": positional_from_registry,
    "actor-app-registry": actor_app_registry_resolution,
    "unknown-actor-name": unknown_actor_name,
    "actor-app-unknown-name": actor_app_unknown_name,
}


def main() -> None:
    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(2)
    _COMMANDS[sys.argv[1]]()


if __name__ == "__main__":
    main()
