namespace Kit.Helpers;

public class TreeNode<TEntity, TKey>
{
    public TKey Id { get; set; }
    public TKey ParentId { get; set; }
    public TEntity Item { get; set; }
    public int Layer { get; set; }
    public List<TreeNode<TEntity, TKey>> ChildNodes { get; set; }
    public bool HasContent { get; set; }
    public TreeNode()
    {
        ChildNodes = new List<TreeNode<TEntity, TKey>>();
    }
}

public static class TreeHelper
{
    /// <summary> Преобразование дерева с родительскими указателями в дерево с дочерними указателями </summary>
    public static IEnumerable<TreeNode<TEntity, TKey>> ConvertToTreeNodes<TEntity, TKey>(this IEnumerable<TEntity> entities, Func<TEntity, TKey> getId, Func<TEntity, TKey> getParentId, Func<TKey, TKey, bool> idEquals, TKey parentIdEmpty)
    {
        IEnumerable<TreeNode<TEntity, TKey>> treeNodes = entities.Select(x => new TreeNode<TEntity, TKey>
        {
            Id = getId(x),
            ParentId = getParentId(x),
            Item = x,
        }).ToList();

        treeNodes.ForEach(treeNode =>
        {
            treeNode.ChildNodes = treeNodes.Where(x => idEquals(treeNode.Id, x.ParentId)).ToList();
        });

        return treeNodes.Where(x => x.ParentId.Equals(parentIdEmpty)).ToList();
    }

    public static List<TreeNode<TEntity, TKey>> FlattenTree<TEntity, TKey, TOrderKey>(this IEnumerable<TreeNode<TEntity, TKey>> nodes, Func<TreeNode<TEntity, TKey>, TOrderKey> getSortKey, int layer = 0)
    {
        var result = new List<TreeNode<TEntity, TKey>>();

        if (nodes.IsNullOrEmpty()) return result;

        foreach (var node in nodes.OrderBy(getSortKey))
        {
            node.Layer = layer;

            result.Add(node);
            result.AddRange(FlattenTree(node.ChildNodes, getSortKey, layer + 1));
        }

        return result;
    }
    public static void SelectSubtreeById<TEntity, TEntityId>(this List<TEntity> nodes, List<TEntity> subtree, TEntityId rootId, Func<TEntity, TEntityId> getId, Func<TEntity, TEntityId> getParentId) where TEntity : class where TEntityId : struct
    {
        TEntity? node = nodes.FirstOrDefault(n => getId(n).Equals(rootId));
        if (node != null)
        {
            subtree.Add(node);
            List<TEntity> children = nodes.Where(n => getParentId(n).Equals(rootId)).ToList();
            foreach (var child in children)
            {
                SelectSubtreeById(nodes, subtree, getId(child), getId, getParentId);
            }
        }
    }
}