from collections.abc import Iterable

from blends.ctx import ctx
from blends.models import (
    NId,
)
from blends.stack.edges import (
    Edge,
    add_edge,
)
from blends.stack.node_helpers import (
    pop_symbol_node_attributes,
    scope_node_attributes,
)
from blends.stack.policies.registry import (
    get_export_policy,
)
from blends.stack.scope_types import ScopeType
from blends.syntax.builders.module_path import compute_module_path_segments
from blends.syntax.builders.utils import (
    get_next_synthetic_node_id,
    get_or_create_root_nid,
)
from blends.syntax.models import (
    SyntaxGraphArgs,
)


def _build_module_definition_path(args: SyntaxGraphArgs, file_scope_id: NId) -> None:
    segments = compute_module_path_segments(args.path)
    if not segments:
        return

    root_nid = get_or_create_root_nid(args)
    previous_id: NId = root_nid
    for index, segment in enumerate(segments):
        segment_id = get_next_synthetic_node_id(args)
        args.syntax_graph.add_node(
            segment_id,
            label_type="SyntheticModulePath",
            **pop_symbol_node_attributes(symbol=segment, is_definition=False),
        )
        add_edge(args.syntax_graph, Edge(source=previous_id, sink=segment_id))

        if index < len(segments) - 1:
            dot_id = get_next_synthetic_node_id(args)
            args.syntax_graph.add_node(
                dot_id,
                label_type="SyntheticModulePath",
                **pop_symbol_node_attributes(symbol=".", is_definition=False),
            )
            add_edge(args.syntax_graph, Edge(source=segment_id, sink=dot_id))
            previous_id = dot_id
        else:
            previous_id = segment_id

    add_edge(args.syntax_graph, Edge(source=previous_id, sink=file_scope_id))


def build_file_node(args: SyntaxGraphArgs, c_ids: Iterable[NId]) -> NId:
    args.syntax_graph.add_node(
        args.n_id,
        label_type="File",
    )

    if ctx.has_feature_flag("StackGraph"):
        policy = get_export_policy(args.language)
        args.syntax_graph.update_node(
            args.n_id,
            scope_node_attributes(
                is_exported=policy.is_scope_exported(
                    args.syntax_graph, args.n_id, ScopeType.FILE.value
                ),
                scope_type=ScopeType.FILE,
            ),
        )
        _build_module_definition_path(args, file_scope_id=args.n_id)

    for c_id in c_ids:
        args.syntax_graph.add_edge(
            args.n_id,
            args.generic(args.fork_n_id(c_id)),
            label_ast="AST",
        )

    return args.n_id
