from blends.ctx import ctx
from blends.models import (
    NId,
)
from blends.query import (
    adj_ast,
    match_ast,
    match_ast_group_d,
)
from blends.syntax.builders.class_decl import (
    build_class_node,
)
from blends.syntax.builders.method_declaration import (
    DirectChildren,
    ListChildren,
    build_method_declaration_node,
)
from blends.syntax.models import (
    SyntaxGraphArgs,
)
from blends.utilities.text_nodes import (
    node_to_str,
)


def _is_classmethod_decorator(args: SyntaxGraphArgs, dec_id: NId) -> bool:
    graph = args.ast_graph
    childs = adj_ast(graph, dec_id)
    if not childs:
        return False
    return node_to_str(graph, childs[-1]) == "classmethod"


def _build_with_classmethod_flag(
    args: SyntaxGraphArgs,
    modifiers_list: list[NId],
    name: str,
    direct: DirectChildren,
    list_children: ListChildren,
) -> NId:
    use_flag = any(
        _is_classmethod_decorator(args, dec_id) for dec_id in modifiers_list
    ) and ctx.has_feature_flag("StackGraph")
    prior = args.metadata.get("is_classmethod")
    if use_flag:
        args.metadata["is_classmethod"] = True
    try:
        return build_method_declaration_node(args, name, direct, list_children)
    finally:
        if use_flag:
            if prior is None:
                args.metadata.pop("is_classmethod", None)
            else:
                args.metadata["is_classmethod"] = prior


def _build_function_node(args: SyntaxGraphArgs, def_id: NId, modifiers_list: list[NId]) -> NId:
    graph = args.ast_graph
    method_node = graph.nodes[def_id]
    name = node_to_str(graph, method_node["label_field_name"])
    block_id = method_node["label_field_body"]
    parameters_id = method_node["label_field_parameters"]
    if "__0__" not in match_ast(graph, parameters_id, "(", ")"):
        parameters_id = None

    direct: DirectChildren = {"block_id": block_id}
    if parameters_id:
        direct["parameters_id"] = parameters_id

    list_children: ListChildren = {}
    if modifiers_list:
        list_children["modifiers_ids"] = modifiers_list

    return _build_with_classmethod_flag(args, modifiers_list, name, direct, list_children)


def reader(args: SyntaxGraphArgs) -> NId:
    graph = args.ast_graph
    n_attrs = graph.nodes[args.n_id]
    def_id = n_attrs["label_field_definition"]
    modifiers_list = match_ast_group_d(graph, args.n_id, "decorator")

    if graph.nodes[def_id]["label_type"] == "function_definition":
        return _build_function_node(args, def_id, modifiers_list)

    class_node = graph.nodes[def_id]
    name = node_to_str(graph, class_node["label_field_name"])
    block_id = class_node["label_field_body"]
    return build_class_node(args, name, block_id, modifiers_list)
