#include "permissions.h"

namespace {
    template <class TRequest>
    void FillPermissionRequest(
        TRequest& request,
        const TString& path,
        const TString& subject,
        const TVector<NSaasLB::EPermission>& permissions
    ) {
        request.mutable_path()->set_path(path);
        auto subjectPermissions = request.add_permissions();
        subjectPermissions->set_subject(subject);
        for (auto permission : permissions) {
            subjectPermissions->add_permission_names(ToString(permission));
        }
    }
}

namespace NSaasLB {
    std::optional<NLogBroker::SingleModifyRequest> TPermissionStorage::Grant(
        const TString& path,
        const TString& subject,
        const TVector<EPermission>& permissions
    ) {
        TVector<EPermission> toGrant;
        auto& currentPermissions = Permissions[path][subject];
        for (auto permission : permissions) {
            if (!currentPermissions.count(permission)) {
                currentPermissions.insert(permission);
                toGrant.push_back(permission);
            }
        }
        if (toGrant) {
            return GetGrantCommand(path, subject, toGrant);
        }
        return {};
    }

    std::optional<NLogBroker::SingleModifyRequest> TPermissionStorage::Revoke(
        const TString& path,
        const TString& subject,
        const TVector<EPermission>& permissions
    ) {
        TVector<EPermission> toRevoke;
        auto permissionsForPath = Permissions.find(path);
        if (permissionsForPath != Permissions.end()) {
            auto permissionsForSubject = permissionsForPath->second.find(subject);
            if (permissionsForSubject != permissionsForPath->second.end()) {
                auto& currentPermissions = permissionsForSubject->second;
                for (auto permission : permissions) {
                    if (currentPermissions.count(permission)) {
                        currentPermissions.erase(permission);
                        toRevoke.push_back(permission);
                    }
                }
            }
        }
        if (toRevoke) {
            return GetRevokeCommand(path, subject, toRevoke);
        }
        return {};
    }

    bool TPermissionStorage::Has(const TString& path, const TString& subject, EPermission permission) const {
        auto permissionsForPath = Permissions.find(path);
        if (permissionsForPath != Permissions.end()) {
            auto permissionsForSubject = permissionsForPath->second.find(subject);
            if (permissionsForSubject != permissionsForPath->second.end()) {
                return permissionsForSubject->second.count(permission);
            }
        }
        return false;
    }

    NLogBroker::SingleModifyRequest TPermissionStorage::GetGrantCommand(
        const TString& path,
        const TString& subject,
        const TVector<EPermission>& permissions
    ) const {
        NLogBroker::SingleModifyRequest request;
        FillPermissionRequest(
            *request.mutable_grant_permissions(),
            path,
            subject,
            permissions
        );
        return request;
    }

    NLogBroker::SingleModifyRequest TPermissionStorage::GetRevokeCommand(
        const TString& path,
        const TString& subject,
        const TVector<EPermission>& permissions
    ) const {
        NLogBroker::SingleModifyRequest request;
        FillPermissionRequest(
            *request.mutable_revoke_permissions(),
            path,
            subject,
            permissions
        );
        return request;
    }

}
