# Re-export EdgeTraversal from faebrykpy
from faebryk.core.faebrykpy import EdgeTraversal as EdgeTraversal
from faebryk.core.zig.gen.faebryk.edgebuilder import EdgeCreationAttributes
from faebryk.core.zig.gen.faebryk.nodebuilder import NodeCreationAttributes
from faebryk.core.zig.gen.graph.graph import BoundNode, GraphView, Literal

class TypeGraphPathError(ValueError):
    """Raised when mount-aware path resolution fails inside the Zig TypeGraph."""

    kind: str
    path: list[str]
    failing_segment: str
    failing_segment_index: int
    index_value: int | None

class TypeGraphInstantiationError(ValueError):
    node: BoundNode
    """The MakeChild or MakeLink node that caused the instantiation failure."""
    kind: str
    identifier: str

class TypeGraphResolveError(ValueError):
    node: BoundNode
    """The reference node that caused the resolution failure."""

    kind: str
    identifier: str

class TypeGraph:
    @staticmethod
    def create(*, g: GraphView) -> "TypeGraph": ...
    @staticmethod
    def of(*, node: BoundNode) -> "TypeGraph": ...
    @staticmethod
    def of_type(*, type_node: BoundNode) -> "TypeGraph | None": ...
    @staticmethod
    def of_instance(*, instance_node: BoundNode) -> "TypeGraph | None": ...
    def add_type(self, *, identifier: str) -> BoundNode:
        """Create and register a new type node.

        Raises:
            ValueError: If a type with this name already exists.
        """
        ...
    def add_make_child(
        self,
        *,
        type_node: BoundNode,
        child_type: BoundNode,
        identifier: str | None,
        node_attributes: NodeCreationAttributes | None = ...,
        soft_create: bool = ...,
    ) -> BoundNode: ...
    def add_make_child_deferred(
        self,
        *,
        type_node: BoundNode,
        child_type_identifier: str,
        identifier: str | None,
        node_attributes: NodeCreationAttributes | None = ...,
        soft_create: bool = ...,
    ) -> BoundNode: ...
    def add_type_reference(self, *, type_identifier: str) -> BoundNode:
        """Create a standalone TypeReference node to be linked later.

        Use this when you need a type reference (e.g., for retype operations)
        that isn't part of a MakeChild. The type reference must be linked
        via Linker.link_type_reference before use.
        """
        ...
    def get_make_child_type_reference(self, *, make_child: BoundNode) -> BoundNode: ...
    def get_make_child_type_reference_by_identifier(
        self, *, type_node: BoundNode, identifier: str
    ) -> BoundNode | None: ...
    def resolve_child_path(
        self, *, start_type: BoundNode, path: list[str]
    ) -> BoundNode | None:
        """
        Walk a child path, resolving each type, return the final type node.

        For path ["inner"] starting at App:
          1. Find "inner" child on App, get its type reference
          2. Resolve type reference to Inner type node via Linker
          3. Return Inner type node

        Returns None if path is empty, any child is not found, or any type fails to
        resolve.
        """
        ...

    class MakeChildNode:
        @staticmethod
        def build(*, value: str | None = ...) -> NodeCreationAttributes: ...

    def debug_add_reference(
        self, *, type_node: BoundNode, path: list[str | EdgeTraversal]
    ) -> BoundNode: ...
    def add_reference(
        self, *, type_node: BoundNode, path: list[str | EdgeTraversal]
    ) -> BoundNode: ...
    def add_make_link(
        self,
        *,
        type_node: BoundNode,
        lhs_reference: BoundNode,
        rhs_reference: BoundNode,
        edge_attributes: EdgeCreationAttributes,
    ) -> BoundNode: ...
    def ensure_child_reference(
        self,
        *,
        type_node: BoundNode,
        path: list[str | EdgeTraversal],
    ) -> BoundNode:
        """Create a reference path for traversing the instance graph.

        Path items can be:
          - str: Composition edge (parent-child relationship)
          - EdgeTraversal: Custom edge type (Trait, Pointer, etc.)

        Example:
            # Composition-only path (backwards compatible)
            tg.ensure_child_reference(type_node=t, path=["child1", "child2"])

            # Mixed path with different edge types
            tg.ensure_child_reference(type_node=t, path=[
                "resistor",
                EdgeTraversal.trait("can_bridge"),
                EdgeTraversal.pointer("in_"),
            ])
        """
        ...
    def collect_pointer_members(
        self,
        *,
        type_node: BoundNode,
        container_path: list[str],
    ) -> list[tuple[int, BoundNode]]: ...
    def collect_make_children(
        self,
        *,
        type_node: BoundNode,
    ) -> list[tuple[str | None, BoundNode]]: ...
    def copy_type_structure(
        self,
        *,
        target: BoundNode,
        source: BoundNode,
        skip_identifiers: list[str] | None = None,
    ) -> None:
        """Copy MakeChild/MakeLink nodes from source into target (for inheritance).

        Args:
            target: The derived type to copy into.
            source: The parent type to copy from.
            skip_identifiers: Identifiers to skip (e.g., auto-generated traits).

        Raises:
            ValueError: If a non-skipped child already exists on target.
        """
        ...
    def collect_make_links(
        self,
        *,
        type_node: BoundNode,
    ) -> list[tuple[BoundNode, tuple[str, ...], tuple[str, ...]]]: ...
    def validate_type(
        self,
        *,
        type_node: BoundNode,
    ) -> list[tuple[BoundNode, str]]:
        """Validate all reference paths in a type node.

        Returns a list of (node, message) tuples for validation errors.
        Empty list means the type is valid.

        Validates:
        - MakeLink endpoints (lhs and rhs paths exist)
        """
        ...
    def get_reference_path(
        self,
        *,
        reference: BoundNode,
    ) -> tuple[str, ...]: ...
    def instantiate(
        self, *, type_identifier: str, attributes: dict[str, Literal]
    ) -> BoundNode: ...
    def instantiate_node(
        self, *, type_node: BoundNode, attributes: dict[str, Literal]
    ) -> BoundNode: ...
    def reference_resolve(
        self, *, reference_node: BoundNode, base_node: BoundNode
    ) -> BoundNode: ...
    def get_graph_view(self) -> GraphView: ...
    def get_self_node(self) -> BoundNode: ...
    def get_type_by_name(self, *, type_identifier: str) -> BoundNode | None: ...
    @staticmethod
    def get_type_name(*, type_node: BoundNode) -> str: ...
    @staticmethod
    def get_type_reference_identifier(*, type_reference: BoundNode) -> str: ...
    def get_or_create_type(self, *, type_identifier: str) -> BoundNode: ...
    def get_type_instance_overview(self) -> list[tuple[str, int]]: ...
    def copy_into(self, *, target_graph: GraphView, minimal: bool) -> "TypeGraph": ...
    @staticmethod
    def copy_node_into(*, start_node: BoundNode, target_graph: GraphView) -> None: ...
