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.file import build_file_node
from blends.syntax.builders.module_path import compute_module_path_segments
from blends.syntax.models import SyntaxGraphArgs, SyntaxMetadata


def _make_file_args(*, path: str, next_synthetic_id: int = 100) -> SyntaxGraphArgs:
    syntax_graph = SyntaxGraph()
    ast_graph = Graph()
    metadata: SyntaxMetadata = {
        "class_path": [],
        "scope_stack": ["1"],
        "next_synthetic_id": next_synthetic_id,
    }

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

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


@pytest.mark.blends_test_group("stack_unittesting")
def test_module_path_simple_module() -> None:
    assert compute_module_path_segments("main.py") == ["main"]


@pytest.mark.blends_test_group("stack_unittesting")
def test_module_path_extracts_stem_from_path_with_directories() -> None:
    assert compute_module_path_segments("pkg/mod.py") == ["mod"]


@pytest.mark.blends_test_group("stack_unittesting")
def test_module_path_extracts_stem_from_absolute_path() -> None:
    assert compute_module_path_segments("/home/user/project/pkg/mod.py") == ["mod"]


@pytest.mark.blends_test_group("stack_unittesting")
def test_module_path_init_file_returns_empty() -> None:
    assert compute_module_path_segments("__init__.py") == []


@pytest.mark.blends_test_group("stack_unittesting")
def test_module_path_init_in_package_returns_empty() -> None:
    assert compute_module_path_segments("pkg/__init__.py") == []


@pytest.mark.blends_test_group("stack_unittesting")
def test_file_node_adds_root_node() -> None:
    args = _make_file_args(path="pkg/mod.py")
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_file_node(args, c_ids=[])

    root_nid = args.metadata.get("root_nid")
    assert root_nid is not None
    assert root_nid in args.syntax_graph.nodes
    assert args.syntax_graph.nodes[root_nid][STACK_GRAPH_KIND] == StackGraphNodeKind.ROOT.value


@pytest.mark.blends_test_group("stack_unittesting")
def test_file_node_chain_connects_root_to_file_scope() -> None:
    args = _make_file_args(path="pkg/mod.py")
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_file_node(args, c_ids=[])

    graph = args.syntax_graph
    root_nid = args.metadata["root_nid"]
    root_successors = list(graph.successors(root_nid))
    assert len(root_successors) == 1
    mod_node = root_successors[0]
    assert graph.nodes[mod_node][STACK_GRAPH_SYMBOL] == "mod"
    assert graph.nodes[mod_node][STACK_GRAPH_KIND] == StackGraphNodeKind.POP_SYMBOL.value

    mod_successors = list(graph.successors(mod_node))
    assert "1" in mod_successors


@pytest.mark.blends_test_group("stack_unittesting")
def test_file_node_absolute_path_uses_stem_only() -> None:
    args = _make_file_args(path="/home/user/project/pkg/mod.py")
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_file_node(args, c_ids=[])

    graph = args.syntax_graph
    root_nid = args.metadata["root_nid"]
    root_successors = list(graph.successors(root_nid))
    assert len(root_successors) == 1
    mod_node = root_successors[0]
    assert graph.nodes[mod_node][STACK_GRAPH_SYMBOL] == "mod"


@pytest.mark.blends_test_group("stack_unittesting")
def test_file_node_no_module_path_when_stack_graph_disabled() -> None:
    args = _make_file_args(path="pkg/mod.py")
    with patch("blends.ctx.ctx.has_feature_flag", return_value=False):
        build_file_node(args, c_ids=[])

    assert args.metadata.get("root_nid") is None


@pytest.mark.blends_test_group("stack_unittesting")
def test_file_node_init_file_does_not_create_root() -> None:
    args = _make_file_args(path="pkg/__init__.py")
    with patch(
        "blends.ctx.ctx.has_feature_flag",
        side_effect=lambda flag: flag == "StackGraph",
    ):
        build_file_node(args, c_ids=[])

    assert args.metadata.get("root_nid") is None
