"""Helper script for actor add YAML-first path Robot integration tests.

Verifies that ``agents actor add`` routes through ActorRegistry.upsert_actor()
with ``yaml_text`` threaded through so that the original YAML text is preserved
in the database while all CLI flags (--set-default, --option) are honoured.
"""

from __future__ import annotations

import json
import sys
import tempfile
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock, patch

from typer.testing import CliRunner

from cleveragents.cli.commands.actor import app as actor_app
from cleveragents.core.exceptions import NotFoundError
from cleveragents.domain.models.core.actor import Actor


def _make_actor(
    *,
    name: str = "local/test-actor",
    provider: str = "openai",
    model: str = "gpt-4o-mini",
    config: dict[str, Any] | None = None,
    yaml_text: str | None = None,
    schema_version: str = "1.0",
) -> Actor:
    blob = config or {}
    return Actor(
        id=1,
        name=name,
        provider=provider,
        model=model,
        config_blob=blob,
        config_hash=Actor.compute_hash(blob),
        graph_descriptor=None,
        unsafe=False,
        is_built_in=False,
        is_default=False,
        yaml_text=yaml_text,
        schema_version=schema_version,
    )


def test_yaml_text_preserved() -> None:
    """Verify registry.upsert_actor() is called with raw yaml_text."""
    runner = CliRunner()
    yaml_content = "name: local/test-actor\nprovider: openai\nmodel: gpt-4o-mini\n"

    with tempfile.NamedTemporaryFile(
        delete=False, suffix=".yaml", mode="w", encoding="utf-8"
    ) as handle:
        handle.write(yaml_content)
        config_path = Path(handle.name)

    try:
        with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
            registry = MagicMock()
            mock_actor = _make_actor(yaml_text=yaml_content)
            registry.upsert_actor.return_value = mock_actor
            registry.get_actor.side_effect = NotFoundError("not found")
            mock_svc.return_value = (MagicMock(), registry)

            result = runner.invoke(
                actor_app,
                ["add", "local/test-actor", "--config", str(config_path)],
            )
    finally:
        config_path.unlink(missing_ok=True)

    assert result.exit_code == 0, (
        f"actor add failed (exit_code={result.exit_code}):\n{result.output}"
    )
    assert registry.upsert_actor.called, "Expected registry.upsert_actor() to be called"
    call_kwargs = registry.upsert_actor.call_args.kwargs
    yaml_text_arg = call_kwargs.get("yaml_text", "")
    assert isinstance(yaml_text_arg, str) and yaml_text_arg.strip(), (
        f"Expected yaml_text to be a non-empty string, got: {yaml_text_arg!r}"
    )
    assert yaml_text_arg == yaml_content, (
        f"yaml_text mismatch.\nExpected: {yaml_content!r}\nGot: {yaml_text_arg!r}"
    )
    print("yaml-text-preserved-ok")


def test_set_default_flag() -> None:
    """Verify --set-default flag passes set_default=True to registry.upsert_actor()."""
    runner = CliRunner()
    yaml_content = "name: local/test-actor\nprovider: openai\nmodel: gpt-4o-mini\n"

    with tempfile.NamedTemporaryFile(
        delete=False, suffix=".yaml", mode="w", encoding="utf-8"
    ) as handle:
        handle.write(yaml_content)
        config_path = Path(handle.name)

    try:
        with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
            registry = MagicMock()
            registry.upsert_actor.return_value = _make_actor(yaml_text=yaml_content)
            registry.get_actor.side_effect = NotFoundError("not found")
            mock_svc.return_value = (MagicMock(), registry)

            result = runner.invoke(
                actor_app,
                [
                    "add",
                    "local/test-actor",
                    "--config",
                    str(config_path),
                    "--set-default",
                ],
            )
    finally:
        config_path.unlink(missing_ok=True)

    assert result.exit_code == 0, (
        f"actor add --set-default failed "
        f"(exit_code={result.exit_code}):\n{result.output}"
    )
    assert registry.upsert_actor.called, "Expected registry.upsert_actor() to be called"
    call_kwargs = registry.upsert_actor.call_args.kwargs
    assert call_kwargs.get("set_default") is True, (
        f"Expected set_default=True in registry.upsert_actor() call, got: {call_kwargs}"
    )
    print("set-default-flag-ok")


def test_option_override_flag() -> None:
    """Verify --option flag passes option_overrides to registry.upsert_actor()."""
    runner = CliRunner()
    yaml_content = "name: local/test-actor\nprovider: openai\nmodel: gpt-4o-mini\n"

    with tempfile.NamedTemporaryFile(
        delete=False, suffix=".yaml", mode="w", encoding="utf-8"
    ) as handle:
        handle.write(yaml_content)
        config_path = Path(handle.name)

    try:
        with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
            registry = MagicMock()
            registry.upsert_actor.return_value = _make_actor(yaml_text=yaml_content)
            registry.get_actor.side_effect = NotFoundError("not found")
            mock_svc.return_value = (MagicMock(), registry)

            result = runner.invoke(
                actor_app,
                [
                    "add",
                    "local/test-actor",
                    "--config",
                    str(config_path),
                    "--option",
                    "temperature=0.5",
                ],
            )
    finally:
        config_path.unlink(missing_ok=True)

    assert result.exit_code == 0, (
        f"actor add --option failed (exit_code={result.exit_code}):\n{result.output}"
    )
    assert registry.upsert_actor.called, "Expected registry.upsert_actor() to be called"
    call_kwargs = registry.upsert_actor.call_args.kwargs
    option_overrides = call_kwargs.get("option_overrides")
    assert option_overrides is not None, (
        f"Expected option_overrides to be set, got: {call_kwargs}"
    )
    assert option_overrides.get("temperature") == 0.5, (
        f"Expected option_overrides['temperature'] == 0.5, got: {option_overrides}"
    )
    print("option-override-flag-ok")


def test_update_flag() -> None:
    """Verify --update flag results in upsert semantics (no duplicate error)."""
    runner = CliRunner()
    config_data = {
        "name": "local/test-actor",
        "provider": "openai",
        "model": "gpt-4o-mini",
    }

    with tempfile.NamedTemporaryFile(
        delete=False, suffix=".json", mode="w", encoding="utf-8"
    ) as handle:
        json.dump(config_data, handle)
        config_path = Path(handle.name)

    try:
        with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
            registry = MagicMock()
            registry.upsert_actor.return_value = _make_actor()
            mock_svc.return_value = (MagicMock(), registry)

            result = runner.invoke(
                actor_app,
                ["add", "local/test-actor", "--config", str(config_path), "--update"],
            )
    finally:
        config_path.unlink(missing_ok=True)

    assert result.exit_code == 0, (
        f"actor add --update failed (exit_code={result.exit_code}):\n{result.output}"
    )
    assert registry.upsert_actor.called, "Expected registry.upsert_actor() to be called"
    print("update-flag-ok")


TESTS = {
    "yaml_text_preserved": test_yaml_text_preserved,
    "set_default_flag": test_set_default_flag,
    "option_override_flag": test_option_override_flag,
    "update_flag": test_update_flag,
}

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: helper_actor_add_yaml_first_path.py <test_name>", file=sys.stderr)
        sys.exit(1)

    test_name = sys.argv[1]
    if test_name not in TESTS:
        print(f"Unknown test: {test_name}", file=sys.stderr)
        sys.exit(1)

    try:
        TESTS[test_name]()
    except AssertionError as exc:
        print(f"ASSERTION FAILED: {exc}", file=sys.stderr)
        sys.exit(1)
    except Exception as exc:
        print(f"ERROR: {exc}", file=sys.stderr)
        sys.exit(1)
