#!/usr/bin/python3
"""Shared tree rendering utilities for slpkg commands (reverse, depends, why)."""

from __future__ import annotations

from collections.abc import Callable
from typing import Any, Optional


def render_tree(  # pylint: disable=[R0913,R0914]
    tree: dict[str, Any],
    direct_parents: Optional[set[str]] = None,
    prefix: str = "",
    depth: int = 0,
    *,
    unicode: bool = True,
    green: str = "",
    grey: str = "",
    endc: str = "",
    color_fn: Optional[Callable[[str], str]] = None,
    label_fn: Optional[Callable[[str], str]] = None,
) -> list[str]:
    """Render a nested dict tree as formatted lines with box-drawing connectors.

    Tree keys are raw package names used for logic (direct_parents lookup,
    color_fn, label_fn).  label_fn controls what gets printed for each node.

    Args:
        tree: Nested dict {name: {child_name: {...}, ...}}.
        direct_parents: Names at depth 0, for the [also direct] annotation.
        prefix: Current indentation prefix (grows with each recursion level).
        depth: Current recursion depth (0 = direct parents of the queried package).
        unicode: Use Unicode box-drawing chars; ASCII fallback otherwise.
        green: ANSI color applied to each node name (default: no color).
        grey: ANSI color used for the [also direct] annotation.
        endc: ANSI reset sequence.
        color_fn: Optional per-name color override; takes precedence over green.
        label_fn: Optional display label override; defaults to the raw name key.

    Returns:
        list[str]: Ready-to-print lines (without a leading indent).
    """
    if unicode:
        branch, last, pipe = "├─ ", "└─ ", "│  "
    else:
        branch, last, pipe = "+- ", "\\- ", "|  "

    if direct_parents is None:
        direct_parents = set()

    lines: list[str] = []
    items = list(tree.items())
    for i, (name, subtree) in enumerate(items):
        is_last = i == len(items) - 1
        connector = last if is_last else branch
        node_color = color_fn(name) if color_fn else green
        display = label_fn(name) if label_fn else name
        annotation = f"  {grey}[also direct]{endc}" if depth > 0 and name in direct_parents else ""
        lines.append(f"{prefix}{connector}{node_color}{display}{endc}{annotation}")
        child_prefix = prefix + ("   " if is_last else pipe)
        lines.extend(
            render_tree(
                subtree,
                direct_parents,
                child_prefix,
                depth + 1,
                unicode=unicode,
                green=green,
                grey=grey,
                endc=endc,
                color_fn=color_fn,
                label_fn=label_fn,
            )
        )
    return lines


def collect_transitive_names(tree: dict[str, Any], _depth: int = 0) -> set[str]:
    """Return the set of all unique package names at depth > 0 in the tree."""
    names: set[str] = set()
    for name, subtree in tree.items():
        if _depth > 0:
            names.add(name)
        names |= collect_transitive_names(subtree, _depth + 1)
    return names
