import tree_sitter
from typing import Dict, List, Any, Optional

TYPE_ABBREVIATIONS = {
    "function_definition": "func",
    "method_declaration": "meth",
    "variable_declaration": "var",
    "lexical_declaration": "lex",
    "class_definition": "class",
    "class_declaration": "class",
    "interface_declaration": "intf",
    "trait_declaration": "trait",
    "namespace_definition": "ns",
    "use_declaration": "use",
    "const_declaration": "const",
    "property_declaration": "prop",
    "identifier": "id",
    "variable_name": "var_id",
    "return_statement": "ret",
    "if_statement": "if",
    "for_statement": "for",
    "while_statement": "while",
    "expression_statement": "expr",
    "call_expression": "call",
    "member_expression": "mem",
    "assignment_expression": "assign",
    "binary_expression": "bin",
    "formal_parameters": "params",
    "parameters": "params",
    "block": "blk",
    "string": "str",
    "number": "num",
    "comment": "cmt",
}

# Terminal nodes that should retain their text content
ESSENTIAL_TERMINALS = {
    "identifier", "property_identifier", "shorthand_property_identifier",
    "type_identifier", "field_identifier", "variable_name",
    "string", "number", "boolean", "null", "undefined",
    "operator", "keyword", "comment", "name"
}

# Nodes that define a body which can be truncated in skeleton mode
BODY_CONTAINERS = {
    "block", "compound_statement", "function_body", "method_body"
}

# Nodes that define functions/methods whose body should be truncated
SKELETON_ROOTS = {
    "function_definition", "method_declaration", "arrow_function",
    "function_declaration", "constructor_declaration", "anonymous_function_creation_expression"
}

def abbreviate(node_type: str) -> str:
    return TYPE_ABBREVIATIONS.get(node_type, node_type)

def minify_node(node: tree_sitter.Node, code: bytes, skeleton: bool = False, in_skeleton_root: bool = False) -> Any:
    node_type = str(node.type)
    
    # Check if we should truncate in skeleton mode
    if skeleton and in_skeleton_root and node_type in BODY_CONTAINERS:
        return {"t": abbreviate(node_type), "trunc": True}

    res: Dict[str, Any] = {"t": abbreviate(node_type)}
    
    # Keep text for essential terminals
    # In tree-sitter 0.23+, nodes without children are leaf nodes
    if node.child_count == 0:
        if node_type in ESSENTIAL_TERMINALS or not node_type.islower():
            res["v"] = node.text.decode("utf-8", errors="ignore")
        return res
    
    # Recurse children
    children = []
    is_new_skeleton_root = node_type in SKELETON_ROOTS
    
    for i in range(node.child_count):
        child = node.child(i)
        if child and not child.is_extra:  # skip comments if they're not essential
             child_min = minify_node(child, code, skeleton, is_new_skeleton_root or (in_skeleton_root and not node_type in BODY_CONTAINERS))
             if child_min:
                 children.append(child_min)
                 
    if children:
        res["c"] = children
        
    return res
