"""Robot Framework helper for actor compiler smoke tests.

Provides a CLI interface for Robot to invoke the actor compiler on
YAML-defined GRAPH actors and inspect compilation metadata.

Usage:
    python robot/helper_actor_compiler.py compile <yaml_file>
    python robot/helper_actor_compiler.py compile-fail <yaml_file>
    python robot/helper_actor_compiler.py metadata <yaml_file>
"""

from __future__ import annotations

import json
import sys
from pathlib import Path

_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from cleveragents.actor.compiler import compile_actor  # noqa: E402
from cleveragents.actor.schema import ActorConfigSchema  # noqa: E402


def main() -> int:
    """Entry point called by Robot Framework ``Run Process``."""
    if len(sys.argv) < 3:
        print("Usage: helper_actor_compiler.py <compile|compile-fail|metadata> <file>")
        return 1

    command = sys.argv[1]
    yaml_path = sys.argv[2]

    if command == "compile":
        try:
            config = ActorConfigSchema.from_yaml_file(yaml_path)
            compiled = compile_actor(config)
            print(f"actor-compiler-ok: {compiled.name}")
            print(f"nodes: {len(compiled.nodes)}")
            print(f"edges: {len(compiled.edges)}")
            print(f"entry: {compiled.entry_point}")
            return 0
        except Exception as exc:
            print(f"actor-compiler-fail: {exc}")
            return 1

    if command == "compile-fail":
        try:
            config = ActorConfigSchema.from_yaml_file(yaml_path)
            compile_actor(config)
            print("actor-compiler-unexpected-success")
            return 1
        except Exception as exc:
            print(f"actor-compiler-expected-fail: {exc}")
            return 0

    if command == "metadata":
        try:
            config = ActorConfigSchema.from_yaml_file(yaml_path)
            compiled = compile_actor(config)
            meta = compiled.metadata.model_dump(mode="json")
            print(f"actor-compiler-metadata: {json.dumps(meta)}")
            return 0
        except Exception as exc:
            print(f"actor-compiler-fail: {exc}")
            return 1

    if command == "cycle-detect":
        # Test cross-actor cycle detection using actor_ref top-level field.
        # Creates two mutually-referencing actors and verifies SubgraphCycleError
        # is raised, confirming the fix for issue #1431.
        from cleveragents.actor.compiler import SubgraphCycleError
        from cleveragents.actor.schema import (
            ActorType,
            EdgeDefinition,
            NodeDefinition,
            NodeType,
            RouteDefinition,
        )

        def _make_actor(
            name: str, subgraph_ref: str | None = None
        ) -> ActorConfigSchema:
            if subgraph_ref is not None:
                nodes = [
                    NodeDefinition(
                        id="start",
                        type=NodeType.AGENT,
                        name="Start",
                        description="Start",
                        config={"agent": "default"},
                    ),
                    NodeDefinition(
                        id="sub",
                        type=NodeType.SUBGRAPH,
                        name="Sub",
                        description="Subgraph",
                        config={},
                        actor_ref=subgraph_ref,
                    ),
                ]
                edges = [EdgeDefinition(from_node="start", to_node="sub")]
                entry, exits = "start", ["sub"]
            else:
                nodes = [
                    NodeDefinition(
                        id="leaf",
                        type=NodeType.AGENT,
                        name="Leaf",
                        description="Leaf",
                        config={"agent": "default"},
                    )
                ]
                edges = []
                entry, exits = "leaf", ["leaf"]
            route = RouteDefinition(
                nodes=nodes, edges=edges, entry_node=entry, exit_nodes=exits
            )
            return ActorConfigSchema(
                name=name,
                type=ActorType.GRAPH,
                description=f"Test actor {name}",
                model="gpt-4",
                route=route,
            )

        actor_a = _make_actor("test/actor-a", subgraph_ref="test/actor-b")
        actor_b = _make_actor("test/actor-b", subgraph_ref="test/actor-a")
        registry = {"test/actor-a": actor_a, "test/actor-b": actor_b}

        try:
            compile_actor(actor_a, actor_resolver=registry.get)
            print(
                "actor-compiler-cycle-not-detected: FAIL — expected SubgraphCycleError"
            )
            return 1
        except SubgraphCycleError as exc:
            print(f"actor-compiler-cycle-detected: {exc}")
            return 0
        except Exception as exc:
            print(f"actor-compiler-unexpected-error: {exc}")
            return 1

    if command == "lsp-binding-field":
        # Regression test for issue #1432: _extract_lsp_bindings() must read
        # from node.lsp_binding (NodeLspBinding typed field), not only config dict.
        from cleveragents.actor.schema import (
            ActorType,
            NodeDefinition,
            NodeLspBinding,
            NodeType,
            RouteDefinition,
        )

        node = NodeDefinition(
            id="coder",
            type=NodeType.AGENT,
            name="Coder",
            description="Code writing node with typed LSP binding",
            config={"agent": "coder_agent"},
            lsp_binding=NodeLspBinding(
                server="local/pyright",
                languages=["python"],
                auto=False,
            ),
        )
        route = RouteDefinition(
            nodes=[node],
            edges=[],
            entry_node="coder",
            exit_nodes=["coder"],
        )
        config = ActorConfigSchema(
            name="workflows/lsp_typed",
            type=ActorType.GRAPH,
            description="Test actor with typed lsp_binding field",
            model="gpt-4",
            route=route,
        )
        try:
            compiled = compile_actor(config)
            bindings = compiled.metadata.lsp_bindings
            if len(bindings) == 1 and bindings[0].lsp_server_name == "local/pyright":
                print("actor-compiler-lsp-binding-ok")
                print(f"lsp-server: {bindings[0].lsp_server_name}")
                return 0
            print(f"actor-compiler-lsp-binding-fail: got {bindings}")
            return 1
        except Exception as exc:
            print(f"actor-compiler-fail: {exc}")
            return 1

    print(f"Unknown command: {command}")
    return 1


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