from __future__ import annotations

from typing import Any

from masfactory.adapters.model import Model, ModelResponseType
from masfactory.components.graphs.graph import Graph
import json

import pytest

from masfactory.components.vibe.compiler import compile_graph_design, load_cached_graph_design
from masfactory.components.vibe.compiler import normalize_graph_design


class DummyModel(Model):
    def __init__(self) -> None:
        super().__init__(model_name="dummy")

    def invoke(  # pragma: no cover - not used in compiler tests
        self,
        messages: list[dict[str, Any]],
        tools: list[dict] | None,
        settings: dict | None = None,
        **kwargs: Any,
    ) -> dict:
        return {"type": ModelResponseType.CONTENT, "content": ""}


def test_vibe_graph_design_allows_nested_loop() -> None:
    graph_design = {
        "nodes": [
            {
                "id": "outer",
                "type": "Loop",
                "label": "Outer Loop",
                "max_iterations": 1,
                "sub_graph": {
                    "nodes": [
                        {
                            "id": "inner",
                            "type": "Loop",
                            "label": "Inner Loop",
                            "max_iterations": 1,
                            "sub_graph": {
                                "nodes": [
                                    {
                                        "id": "work",
                                        "type": "Action",
                                        "label": "Work",
                                        "agent": "work_agent",
                                        "instructions": "Do one unit of work.",
                                    }
                                ],
                                "edges": [
                                    {"source": "CONTROLLER", "target": "work", "key": {}},
                                    {"source": "work", "target": "CONTROLLER", "key": {}},
                                ],
                            },
                        }
                    ],
                    "edges": [
                        {"source": "CONTROLLER", "target": "inner", "key": {}},
                        {"source": "inner", "target": "CONTROLLER", "key": {}},
                    ],
                },
            }
        ],
        "edges": [
            {"source": "ENTRY", "target": "outer", "key": {}},
            {"source": "outer", "target": "EXIT", "key": {}},
        ],
    }

    g = Graph(name="test")
    compile_graph_design(target_graph=g, graph_design=graph_design, model=DummyModel(), tools=None)
    g.build()


def test_compile_graph_design_rejects_file_path() -> None:
    g = Graph(name="test")
    with pytest.raises(TypeError):
        compile_graph_design(target_graph=g, graph_design="graph_design.json", model=DummyModel(), tools=None)  # type: ignore[arg-type]


def test_load_cached_graph_design_returns_object(tmp_path) -> None:
    graph_design_path = tmp_path / "graph_design.json"
    graph_design_path.write_text(
        json.dumps(
            {
                "nodes": [
                    {
                        "id": "work",
                        "type": "Action",
                        "label": "Work",
                        "agent": "work_agent",
                        "instructions": "Do one unit of work.",
                    }
                ],
                "edges": [
                    {"source": "ENTRY", "target": "work", "key": {}},
                    {"source": "work", "target": "EXIT", "key": {}},
                ],
            }
        ),
        encoding="utf-8",
    )

    loaded = load_cached_graph_design(graph_design_path)
    assert isinstance(loaded, dict)
    assert isinstance(loaded.get("nodes"), list)
    assert isinstance(loaded.get("edges"), list)


def test_normalize_graph_design_accepts_nested_graph_wrapper() -> None:
    graph_design = {
        "graph_design": {
            "graph": {
                "nodes": [
                    {
                        "id": "work",
                        "type": "Action",
                        "label": "Work",
                        "agent": "work_agent",
                        "instructions": "Do one unit of work.",
                    }
                ],
                "edges": [
                    {"source": "ENTRY", "target": "work", "key": {}},
                    {"source": "work", "target": "EXIT", "key": {}},
                ],
            }
        }
    }

    normalized = normalize_graph_design(graph_design)
    assert isinstance(normalized.get("nodes"), list)
    assert isinstance(normalized.get("edges"), list)
