#include "permissions_tree_helpers.h"

#include <algorithm>

namespace maps::wiki::aclsrv {

namespace {
/**
 * This function constructs tree of permission names
 * by inserting each permission path from the root
 * algorithm doesn't add children to exisitng leafs
 * algorithm drops children if one of path need leaf
 * at branch node
 */
void
addToTree(const std::list<std::string>& path, std::list<TreeNode>* roots)
{
    std::list<TreeNode>* curLevel = roots;
    for (auto nameIt = path.begin(); nameIt != path.end(); ++nameIt) {
        auto it = std::find_if(curLevel->begin(), curLevel->end(),
            [&nameIt](const TreeNode& n){return n.name == *nameIt;});
        if (it != curLevel->end()) {
            // Check if we need to convert node to leaf
            // or going to create sub-tree at leaf
            if (nameIt == std::prev(path.end()) || it->children.empty()) {
                it->children.clear();
                return;
            }
            curLevel = &it->children;
        } else {
            curLevel->push_back(TreeNode(*nameIt));
            curLevel = &curLevel->back().children;
        }
    }
}


void
collapseFullPaths(
    const acl::Permissions& allPermissions,
    const std::list<std::string>& path,
    TreeNode& node)
{
    if (node.children.empty()) {
        return;
    }
    auto thisPath = path;
    thisPath.push_back(node.name);
    bool allChildsEmpty = true;
    for (auto& child : node.children) {
        collapseFullPaths(allPermissions, thisPath, child);
        allChildsEmpty = allChildsEmpty && child.children.empty();
    }
    if (!allChildsEmpty) {
        return;
    }
    if (node.children.size() ==
        allPermissions.children(
            allPermissions.permission(thisPath)).size())
    {
        node.children.clear();
    }
}

void
collapseFullPaths(
    const acl::Permissions& allPermissions,
    std::list<TreeNode>& roots)
{
    for (auto& root : roots) {
        collapseFullPaths(allPermissions, {}, root);
    }
}
} // namespace

std::list<TreeNode>
buildPermissionsTree(
    const acl::Permissions& allPermissions,
    const std::vector<acl::Permission>& permissions)
{
    std::list<TreeNode> roots;
    for (const auto& permission : permissions) {
        addToTree(allPermissions.path(permission), &roots);
    }
    collapseFullPaths(allPermissions, roots);
    return roots;
}

} // namespace maps::wiki::aclsrv
