from __future__ import annotations

from pathlib import Path
from typing import Any, Callable

from masfactory import Graph, Model, Node, RootGraph, template_defaults
from masfactory.utils.hook import masf_hook

import os
from .compiler import compile_graph_design, load_cached_graph_design
from .vibe_workflow import VibeWorkflow

class VibeGraph(Graph):
    """
    VibeGraphing:
    - Accept a workflow object (no registry).
    - Workflow is responsible for parsing its own raw output into canonical graph_design.
    - VibeGraph is responsible for caching and compiling into runnable nodes/edges.
    """

    def __init__(
        self,
        name: str,
        invoke_model: Model,
        *,
        build_instructions: str,
        build_model: Model,
        build_workflow: RootGraph = VibeWorkflow,
        build_cache_path: str | Path | None = None,
        invoke_tools: list[Callable] | None = None,
        pull_keys: dict[str, dict|str] | None = None,
        push_keys: dict[str, dict|str] | None = None,
        attributes: dict[str, object] | None = None,
    ):
        """Create a VibeGraph.

        Args:
            name: Graph name.
            invoke_model: Model used by compiled agents for step execution.
            build_instructions: Instructions used by the build workflow to produce graph_design.
            build_model: Model used by the build workflow.
            build_workflow: RootGraph workflow that produces a graph_design artifact.
            build_cache_path: Optional cache file path for graph_design.
            invoke_tools: Optional list of tool callables available to compiled agents.
            pull_keys: Attribute pull rule for this graph.
            push_keys: Attribute push rule for this graph.
            attributes: Default attributes for this graph.
        """
        super().__init__(name=name, pull_keys=pull_keys, push_keys=push_keys, attributes=attributes)
        self._invoke_model = invoke_model
        self._build_workflow = build_workflow
        self._build_instructions = build_instructions
        self._build_model = build_model
        self._build_cache_path = build_cache_path
        self._invoke_tools = invoke_tools

    @masf_hook(Node.Hook.BUILD)
    def build(self):
        """Build the graph by producing (or loading) a graph_design and compiling it.

        - If `build_cache_path` is missing, it runs `build_workflow` to generate graph_design and caches it.
        - Otherwise, it loads the cached graph_design.
        - It then compiles the design into runnable nodes/edges on this graph.
        """
        tools = list(self._invoke_tools or [])
        graph_design = {}
        # build graph design
        if self._build_cache_path is None or not os.path.exists(self._build_cache_path):
            with template_defaults(
                model=self._build_model,
                file_fields={"graph_design": self._build_cache_path}
            ):
                self._build_workflow.build()
                
                message,attributes = self._build_workflow.invoke(
                    {
                        "build_instructions": self._build_instructions,
                        "user_demand": self._build_instructions,
                        "user_advice": "",
                        "system_advice": "",
                    }
                )
                graph_design = attributes.get("graph_design", "")
                
        else:
            graph_design = load_cached_graph_design(self._build_cache_path)

        # compile graph design
        compile_graph_design(
            target_graph=self,
            graph_design=graph_design,
            model=self._invoke_model,
            tools=tools,
        )
        super().build()


__all__ = ["VibeGraph"]
