from __future__ import annotations

from typing_extensions import TypeAlias

from ncae_sdk.utils.jinja2_tplgen._ast_collect import IterablePath, VariablePath, collect_variables
from ncae_sdk.utils.jinja2_tplgen._service_models import GroupTemplate, Template, TextListTemplate, TextTemplate

# Recursive mapping representing a variable namespace tree.
NodeTree: TypeAlias = dict[str, "NodeTree"]


def to_service_template(jinja2_template: str) -> list[Template]:
    """
    Converts the specified Jinja2 template, specified as a string, into a NCAE service template.
    All variables are turned into appropriate fields to match the structure of the Jinja2 template.
    """
    paths, iterables = collect_variables(jinja2_template)
    path_list = sorted(paths)
    iterable_list = sorted(iterables)
    tree = _build_node_tree(path_list)

    return _to_templates(tree, iterable_list)


def _build_node_tree(paths: list[VariablePath]) -> NodeTree:
    """
    Expands the flat tuple paths from the variable collector into a nested dict tree structure.
    Example input: [('user', 'name'), ('user', 'email'), ('settings', 'tz')]
    Example output: {'user': {'name': {}, 'email': {}}, 'settings': {'tz': {}}}
    """
    tree: NodeTree = {}
    for path in paths:
        current: NodeTree = tree
        for part in path:
            current = current.setdefault(part, {})

    return tree


def _to_templates(tree: NodeTree, iterables: list[IterablePath]) -> list[Template]:
    """
    Transforms a node tree into a list of NCAE service template fields with appropriate types.
    """
    iterable_set = set(iterables)

    def node_to_template(name: str, subtree: NodeTree, path: VariablePath) -> Template:
        label = _humanize_name_to_label(name)

        # If this node has its own subtree, treat it as a group input with children
        # This applies both to lists and dicts, in the latter case a separate text input is created for each leaf node
        if subtree:
            children = [
                node_to_template(child_name, child_tree, path + (child_name,))
                for child_name, child_tree in sorted(subtree.items())
            ]
            return GroupTemplate(type="group", name=name, label=label, children=children)

        # If this node is itself part of an iterable, then the input must be a text-list to allow multiple values
        # E.g. a top-level "{% for p in port %}{{ port }}{% endfor %}"
        if path in iterable_set:
            return TextListTemplate(type="text-list", name=name, label=label)

        # In all other cases, default to a text input
        return TextTemplate(type="text", name=name, label=label)

    return [node_to_template(root_name, subtree, (root_name,)) for root_name, subtree in sorted(tree.items())]


def _humanize_name_to_label(name: str) -> str:
    """
    Tries to convert a snake_case or kebab-case name into a human-friendly label.
    E.g., "interface_name" -> "Interface name"
    """
    s = name.replace("_", " ").replace("-", " ")
    return s[:1].upper() + s[1:]
