#!/usr/bin/env python3
"""Helper script for actor add v3 schema validation robot tests.

Tests the agents actor add CLI command with v3 YAML files to ensure
ActorConfigSchema validation is applied (cycle detection, required fields, etc.).

This helper delegates to the real CLI binary so that it exercises the actual
command path — not internal helpers.
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path


def add_actor(actor_name: str, config_file: str) -> int:
    """Test adding an actor via the real CLI binary.

    Invokes ``agents actor add <actor_name> --config <config_file>`` as a
    subprocess so that all CLI-level validation (including v3 schema
    validation) runs for real.

    Args:
        actor_name: The actor name (e.g., local/test-llm)
        config_file: Path to the YAML config file

    Returns:
        0 on success, 1 on validation failure or timeout
    """
    config_path = Path(config_file)
    if not config_path.exists():
        print(
            f"actor-add-error: Config file not found: {config_file}",
            file=sys.stderr,
        )
        return 1

    try:
        result = subprocess.run(
            ["agents", "actor", "add", actor_name, "--config", config_file],
            capture_output=True,
            text=True,
            timeout=60,
        )
    except (subprocess.TimeoutExpired, FileNotFoundError) as exc:
        print(f"actor-add-error: command failed: {exc}", file=sys.stderr)
        return 1

    if result.returncode == 0:
        print("actor-add-success")
        return 0
    else:
        output = result.stdout + result.stderr
        print(f"actor-add-error: {output.strip()}", file=sys.stderr)
        return 1


def update_actor(actor_name: str, config_file: str) -> int:
    """Test updating an actor via the real CLI binary.

    Invokes ``agents actor update <actor_name> --config <config_file>`` as a
    subprocess so that all CLI-level validation (including v3 schema
    validation) runs for real.

    Args:
        actor_name: The actor name (e.g., local/test-llm)
        config_file: Path to the YAML config file

    Returns:
        0 on success, 1 on validation failure or timeout
    """
    config_path = Path(config_file)
    if not config_path.exists():
        print(
            f"actor-update-error: Config file not found: {config_file}",
            file=sys.stderr,
        )
        return 1

    try:
        result = subprocess.run(
            ["agents", "actor", "update", actor_name, "--config", config_file],
            capture_output=True,
            text=True,
            timeout=60,
        )
    except (subprocess.TimeoutExpired, FileNotFoundError) as exc:
        print(f"actor-update-error: command failed: {exc}", file=sys.stderr)
        return 1

    if result.returncode == 0:
        print("actor-update-success")
        return 0
    else:
        output = result.stdout + result.stderr
        print(f"actor-update-error: {output.strip()}", file=sys.stderr)
        return 1


def show_actor(actor_name: str) -> int:
    """Test showing an actor via the real CLI binary.

    Invokes ``agents actor show <actor_name> --format json`` as a
    subprocess to verify the actor was persisted and is retrievable.

    Args:
        actor_name: The actor name (e.g., local/test-llm)

    Returns:
        0 on success, 1 on failure or timeout
    """
    try:
        result = subprocess.run(
            ["agents", "actor", "show", actor_name, "--format", "json"],
            capture_output=True,
            text=True,
            timeout=60,
        )
    except (subprocess.TimeoutExpired, FileNotFoundError) as exc:
        print(f"actor-show-error: command failed: {exc}", file=sys.stderr)
        return 1

    if result.returncode == 0:
        print(f"actor-show-success: {result.stdout.strip()}")
        return 0
    else:
        output = result.stdout + result.stderr
        print(f"actor-show-error: {output.strip()}", file=sys.stderr)
        return 1


def build_v3_reactive_config(config_file: str) -> int:
    """Test that a v3 actor config blob can be parsed by ReactiveConfigParser.

    This exercises the ``_build_from_v3()`` execution path that converts
    a v3 ``ActorConfigSchema`` blob into a ``ReactiveConfig`` with
    synthesised agents and routes.

    Args:
        config_file: Path to the YAML config file

    Returns:
        0 on success, 1 on failure
    """
    _src = str(Path(__file__).resolve().parents[0].parent / "src")
    if _src not in sys.path:
        sys.path.insert(0, _src)

    try:
        import yaml as _yaml

        from cleveragents.reactive.config_parser import ReactiveConfigParser
    except ImportError as exc:
        print(f"build-v3-config-error: import failed: {exc}", file=sys.stderr)
        return 1

    config_path = Path(config_file)
    if not config_path.exists():
        print(
            f"build-v3-config-error: Config file not found: {config_file}",
            file=sys.stderr,
        )
        return 1

    text = config_path.read_text(encoding="utf-8")
    blob = _yaml.safe_load(text) or {}

    parser = ReactiveConfigParser()
    try:
        rc = parser._build(blob)
    except Exception as exc:
        print(f"build-v3-config-error: _build failed: {exc}", file=sys.stderr)
        return 1

    actor_type = blob.get("type", "").lower()
    if actor_type == "llm":
        if len(rc.agents) != 1:
            print(
                f"build-v3-config-error: expected 1 agent, got {len(rc.agents)}",
                file=sys.stderr,
            )
            return 1
        agent = next(iter(rc.agents.values()))
        if not agent.config.get("model"):
            print(
                "build-v3-config-error: agent config missing model",
                file=sys.stderr,
            )
            return 1
        if not agent.config.get("provider"):
            print(
                "build-v3-config-error: agent config missing provider",
                file=sys.stderr,
            )
            return 1
        # Validate skills propagation when present in source blob.
        src_skills = blob.get("skills") or []
        if src_skills and not agent.config.get("skills"):
            print(
                "build-v3-config-error: agent config missing skills",
                file=sys.stderr,
            )
            return 1
        # Validate system_prompt propagation when present in source blob.
        if blob.get("system_prompt") and not agent.config.get("system_prompt"):
            print(
                "build-v3-config-error: agent config missing system_prompt",
                file=sys.stderr,
            )
            return 1
        # Validate provider inference from model string.
        model_str = blob.get("model", "")
        if "/" in str(model_str):
            expected_provider = str(model_str).split("/", 1)[0]
            actual_provider = agent.config.get("provider", "")
            if actual_provider != expected_provider:
                print(
                    f"build-v3-config-error: expected provider "
                    f"'{expected_provider}', got '{actual_provider}'",
                    file=sys.stderr,
                )
                return 1
        print("build-v3-config-success")
    elif actor_type == "graph":
        if len(rc.agents) < 1:
            print(
                f"build-v3-config-error: expected >=1 agent, got {len(rc.agents)}",
                file=sys.stderr,
            )
            return 1
        if len(rc.routes) < 1:
            print(
                f"build-v3-config-error: expected >=1 route, got {len(rc.routes)}",
                file=sys.stderr,
            )
            return 1
        route = next(iter(rc.routes.values()))
        if route.type.value != "graph":
            print(
                f"build-v3-config-error: expected graph route, got {route.type.value}",
                file=sys.stderr,
            )
            return 1
        # Validate entry_point matches source blob.
        route_blob = blob.get("route") or {}
        expected_entry = route_blob.get("entry_node", "start")
        if route.entry_point != expected_entry:
            print(
                f"build-v3-config-error: expected entry_point "
                f"'{expected_entry}', got '{route.entry_point}'",
                file=sys.stderr,
            )
            return 1
        # Validate edge count matches source blob.
        expected_edges = len(route_blob.get("edges") or [])
        if len(route.edges) != expected_edges:
            print(
                f"build-v3-config-error: expected {expected_edges} "
                f"edges, got {len(route.edges)}",
                file=sys.stderr,
            )
            return 1
        # Validate node IDs are present in agents dict.
        for node in route_blob.get("nodes") or []:
            nid = node.get("id", "") if isinstance(node, dict) else ""
            if nid and nid not in rc.agents:
                print(
                    f"build-v3-config-error: node '{nid}' not in agents",
                    file=sys.stderr,
                )
                return 1
        print("build-v3-config-success")
    else:
        print(
            f"build-v3-config-error: unsupported type '{actor_type}'",
            file=sys.stderr,
        )
        return 1

    return 0


def main() -> int:
    """Main entry point."""
    if len(sys.argv) < 2:
        print(
            "Usage: helper_actor_add_v3_schema_validation.py"
            " <command> [<actor_name>] [<config_file>]"
        )
        return 1

    command = sys.argv[1]

    if command in ("add", "update", "show") and len(sys.argv) < 3:
        print(f"Error: '{command}' requires <actor_name>", file=sys.stderr)
        return 1

    if command in ("add", "update") and len(sys.argv) < 4:
        print(
            f"Error: '{command}' requires <actor_name> <config_file>",
            file=sys.stderr,
        )
        return 1

    if command == "add":
        return add_actor(sys.argv[2], sys.argv[3])
    elif command == "update":
        return update_actor(sys.argv[2], sys.argv[3])
    elif command == "show":
        return show_actor(sys.argv[2])
    elif command == "build-v3-config":
        if len(sys.argv) < 3:
            print(
                "Error: 'build-v3-config' requires <config_file>",
                file=sys.stderr,
            )
            return 1
        return build_v3_reactive_config(sys.argv[2])
    else:
        print(f"Unknown command: {command}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    sys.exit(main())
