from typing import List

from robotframework_ls.impl.protocols import ICompletionContext
from robocorp_ls_core.lsp import DocumentSymbolTypedDict, SymbolKind


def collect_children(ast) -> List[DocumentSymbolTypedDict]:
    from robotframework_ls.impl import ast_utils
    from robot.api import Token
    from robotframework_ls.impl.ast_utils import create_range_from_token

    ret: List[DocumentSymbolTypedDict] = []

    for node_info in ast_utils.iter_nodes(
        ast, accept_class=("Keyword", "TestCase", "Variable")
    ):
        node = node_info.node
        classname = node.__class__.__name__
        if classname == "Keyword":
            token = node.header.get_token(Token.KEYWORD_NAME)
            symbol_range = create_range_from_token(token)
            doc_symbol: DocumentSymbolTypedDict = {
                "name": str(token),
                "kind": SymbolKind.Function,
                "range": symbol_range,
                "selectionRange": symbol_range,
            }
            ret.append(doc_symbol)

        elif classname == "TestCase":
            token = node.header.get_token(Token.TESTCASE_NAME)
            symbol_range = create_range_from_token(token)
            doc_symbol = {
                "name": str(token),
                "kind": SymbolKind.Class,
                "range": symbol_range,
                "selectionRange": symbol_range,
            }
            ret.append(doc_symbol)

        elif classname == "Variable":
            token = node.get_token(Token.VARIABLE)
            symbol_range = create_range_from_token(token)
            doc_symbol = {
                "name": str(token),
                "kind": SymbolKind.Variable,
                "range": symbol_range,
                "selectionRange": symbol_range,
            }
            ret.append(doc_symbol)

    return ret


def create_section_doc_symbol(
    ret: List[DocumentSymbolTypedDict], ast, header_token_type, symbol_kind
):
    from robotframework_ls.impl.ast_utils import create_range_from_token

    token = ast.header.get_token(header_token_type)
    if token is not None:
        symbol_range = create_range_from_token(token)
        doc_symbol: DocumentSymbolTypedDict = {
            "name": str(token).replace("*", "").strip(),
            "kind": symbol_kind,
            "range": symbol_range,
            "selectionRange": symbol_range,
            "children": collect_children(ast),
        }
        ret.append(doc_symbol)


def document_symbol(
    completion_context: ICompletionContext,
) -> List[DocumentSymbolTypedDict]:
    from robotframework_ls.impl import ast_utils
    from robot.api import Token

    ret: List[DocumentSymbolTypedDict] = []
    ast = completion_context.get_ast()

    for node_info in ast_utils.iter_nodes(ast, "SettingSection"):
        create_section_doc_symbol(
            ret, node_info.node, Token.SETTING_HEADER, SymbolKind.Namespace
        )

    for node_info in ast_utils.iter_nodes(ast, "VariableSection"):
        create_section_doc_symbol(
            ret, node_info.node, Token.VARIABLE_HEADER, SymbolKind.Namespace
        )

    for node_info in ast_utils.iter_nodes(ast, "TestCaseSection"):
        create_section_doc_symbol(
            ret, node_info.node, Token.TESTCASE_HEADER, SymbolKind.Namespace
        )

    for node_info in ast_utils.iter_nodes(ast, "KeywordSection"):
        create_section_doc_symbol(
            ret, node_info.node, Token.KEYWORD_HEADER, SymbolKind.Namespace
        )

    return ret
