#include <maps/wikimap/mapspro/libs/acl/include/permissions.h>
#include <maps/wikimap/mapspro/libs/acl/include/exception.h>

namespace maps::wiki::acl {

Permissions::Permissions(std::vector<Permission> data)
    : data_(std::move(data))
{
    for (auto it = data_.begin(); it != data_.end(); ++it) {
        id2ptr_.insert({it->id(), it});
    }
}

std::vector<Permission>
Permissions::roots() const
{
    std::vector<Permission> result;
    for (const auto& permission : data_) {
        if (!permission.parentId()) {
            result.push_back(permission);
        }
    }
    return result;
}

std::vector<Permission>
Permissions::children(const Permission& permission) const
{
    auto id = permission.id();
    checkedPermission(id);
    std::vector<Permission> result;
    for (const auto& perm : data_) {
        if (perm.parentId() == id) {
            result.push_back(perm);
        }
    }
    return result;
}

std::list<std::string>
Permissions::path(const Permission& permission) const
{
    std::list<std::string> result;
    for (auto id = permission.id(); id; ) {
        const auto& checkedPerm = checkedPermission(id);
        result.push_front(checkedPerm.name());
        id = checkedPerm.parentId();
    }
    return result;
}

Permission
Permissions::permission(const std::list<std::string>& path) const
{
    if (path.empty() || path.front().empty()) {
        throw BadParam() << "Bad path";
    }
    auto partIt = path.begin();
    auto permission = rootPermission(*partIt);
    while (++partIt != std::end(path)) {
        permission = childPermission(permission, *partIt);
    }
    return permission;
}

const Permission&
Permissions::checkedPermission(ID id) const
{
    auto it = id2ptr_.find(id);
    if (it == id2ptr_.end()) {
        throw PermissionNotExists()
            << "Permission " << id << " doesn't exist";
    }
    return *(it->second);
}

Permission
Permissions::rootPermission(const std::string& name) const
{
    for (const auto& permission : data_) {
        if (!permission.parentId() && permission.name() == name) {
            return permission;
        }
    }
    throw PermissionNotExists()
        << "Root permission " << name << " not found";
}

Permission
Permissions::childPermission(
    const Permission& permission, const std::string& childName) const
{
    auto id = permission.id();
    checkedPermission(id);
    for (const auto& perm : data_) {
        if (perm.parentId() == id && perm.name() == childName) {
            return perm;
        }
    }
    throw PermissionNotExists() << "Permission " << id
        << " doesn't have child with name " << childName;
}

} // namespace maps::wiki::acl
