from unittest.mock import (
    patch,
)

import pytest

from blends.models import (
    Graph,
    Language,
    NId,
    SyntaxGraph,
)
from blends.stack.attributes import STACK_GRAPH_KIND, STACK_GRAPH_SYMBOL
from blends.stack.node_kinds import (
    StackGraphNodeKind,
)
from blends.syntax.builders.member_access import (
    build_member_access_node,
)
from blends.syntax.models import (
    SyntaxGraphArgs,
    SyntaxMetadata,
)


@pytest.fixture  # type: ignore[misc,attr-defined]
def mock_args() -> SyntaxGraphArgs:  # type: ignore[misc]
    syntax_graph = SyntaxGraph()
    ast_graph = Graph()
    metadata: SyntaxMetadata = {
        "class_path": [],
        "scope_stack": ["1"],
        "next_synthetic_id": 100,
    }
    syntax_graph.add_node("1", label_type="File")
    syntax_graph.add_node("5", label_type="SymbolLookup", symbol="obj")

    def generic(args: SyntaxGraphArgs) -> NId:
        return args.n_id

    return SyntaxGraphArgs(
        generic=generic,
        path="test.py",
        language=Language.PYTHON,
        ast_graph=ast_graph,
        syntax_graph=syntax_graph,
        n_id="2",
        metadata=metadata,
    )


@pytest.mark.blends_test_group("stack_unittesting")
def test_member_access_creates_push_symbol_for_member(
    mock_args: SyntaxGraphArgs,
) -> None:
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_member_access_node(
            mock_args,
            member="attr",
            expression="obj.attr",
            expression_id="5",
        )

    assert (
        mock_args.syntax_graph.nodes["2"][STACK_GRAPH_KIND] == StackGraphNodeKind.PUSH_SYMBOL.value
    )
    assert mock_args.syntax_graph.nodes["2"][STACK_GRAPH_SYMBOL] == "attr"


@pytest.mark.blends_test_group("stack_unittesting")
def test_member_access_creates_dot_separator_node(
    mock_args: SyntaxGraphArgs,
) -> None:
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_member_access_node(
            mock_args,
            member="attr",
            expression="obj.attr",
            expression_id="5",
        )

    graph = mock_args.syntax_graph
    dot_nodes = [
        s
        for s in graph.successors("2")
        if graph.nodes[s].get(STACK_GRAPH_KIND) == StackGraphNodeKind.PUSH_SYMBOL.value
        and graph.nodes[s].get(STACK_GRAPH_SYMBOL) == "."
    ]
    assert len(dot_nodes) == 1
    dot_node = dot_nodes[0]
    assert graph.has_edge(dot_node, "5")


@pytest.mark.blends_test_group("stack_unittesting")
def test_member_access_no_stack_graph_when_feature_disabled(
    mock_args: SyntaxGraphArgs,
) -> None:
    with patch("blends.ctx.ctx.has_feature_flag", return_value=False):
        build_member_access_node(
            mock_args,
            member="attr",
            expression="obj.attr",
            expression_id="5",
        )

    assert STACK_GRAPH_KIND not in mock_args.syntax_graph.nodes["2"]
    assert STACK_GRAPH_SYMBOL not in mock_args.syntax_graph.nodes["2"]
