#include <maps/wikimap/mapspro/libs/acl_utils/include/feedback.h>

#include <maps/wikimap/mapspro/libs/acl/include/check_context.h>
#include <maps/wikimap/mapspro/libs/acl/include/exception.h>
#include <maps/wikimap/mapspro/libs/acl/include/subject_path.h>
#include <maps/libs/log8/include/log8.h>

namespace maps::wiki::acl_utils {

namespace {

const std::string HIDDEN_TASK_PERMISSION = "mpro/social/feedback/task/hidden";
const std::string MRC_VIEW_HIDDEN_PERMISSION = "mpro/mrc/photos/view-hidden";

const acl::SubjectPath HIDDEN_TASK_PERMISSION_PATH(HIDDEN_TASK_PERMISSION);
const acl::SubjectPath MRC_VIEW_HIDDEN_PERMISSION_PATH(MRC_VIEW_HIDDEN_PERMISSION);

bool hasOperation(
    const sf::History& history,
    social::TUid uid,
    const sf::TaskOperations& operations)
{
    for (const auto& item : history.items()) {
        if (item.modifiedBy() == uid && operations.contains(item.operation())) {
            return true;
        }
    }
    return false;
}

bool isCreatorOfFeedbackTask(
    const sf::History& history,
    social::TUid uid)
{
    static const sf::TaskOperations CREATE_OPERATION = {sf::TaskOperation::Create};
    return hasOperation(history, uid, CREATE_OPERATION);
}

bool isModifierOfFeedbackTask(
    const sf::History& history,
    social::TUid uid)
{
    static const sf::TaskOperations MODIFY_OPERATIONS = {
        sf::TaskOperation::Accept,
        sf::TaskOperation::Reject,
        sf::TaskOperation::NeedInfo,
        sf::TaskOperation::ProcessingLevelUp,
        sf::TaskOperation::ProcessingLevelDown,
        sf::TaskOperation::ChangeProcessingLvl,
        sf::TaskOperation::ChangePosition,
        sf::TaskOperation::ChangeType,
    };
    return hasOperation(history, uid, MODIFY_OPERATIONS);
}

bool isAcquiredByUser(
    const sf::Task& task,
    social::TUid uid)
{
    return task.acquired() && task.acquired()->uid == uid;
}

} // namespace

FeedbackChecker::FeedbackChecker(
    CachingAclChecker& aclChecker,
    const FeedbackPresetChecker& fbPresetChecker)
    : aclChecker_(aclChecker)
    , fbPresetChecker_(fbPresetChecker)
{}

bool
FeedbackChecker::isAllowedToView(
    const sf::Task& task,
    const sf::History& history,
    acl::UID uid) const try
{
    if (task.internalContent() && !aclChecker_.userHasPermission(uid, MRC_VIEW_HIDDEN_PERMISSION)) {
        return false;
    }
    if (isCreatorOfFeedbackTask(history, uid)) {
        return true;
    }
    if (isAcquiredByUser(task, uid)) {
        return true;
    }
    if (task.hidden() && !aclChecker_.userHasPermission(uid, HIDDEN_TASK_PERMISSION))
    {
        return false;
    }
    if (isModifierOfFeedbackTask(history, uid)) {
        return true;
    }
    if (!fbPresetChecker_.isInAssignedPreset(uid, task, PresetRightKind::View)) {
        return false;
    }
    return true;
} catch (const std::exception& ex) {
    WARN() << "isAllowedToView by acl-checker, uid: " << uid << " : " << ex.what();
    return false;
}

bool
FeedbackChecker::isAllowedToModify(
    const sf::Task& task,
    acl::UID uid) const try
{
    if (task.hidden() && !aclChecker_.userHasPermission(uid, HIDDEN_TASK_PERMISSION)) {
        return false;
    }
    if (task.internalContent() && !aclChecker_.userHasPermission(uid, MRC_VIEW_HIDDEN_PERMISSION)) {
        return false;
    }
    if (!fbPresetChecker_.isInAssignedPreset(uid, task, PresetRightKind::Modify)) {
        return false;
    }
    return true;
} catch (const std::exception& ex) {
    WARN() << "isAllowedToModify by acl-checker, uid: " << uid << " : " << ex.what();
    return false;
}

sf::TaskOperations FeedbackChecker::restrictValidOperations(
    const sf::TaskOperations& operations,
    const sf::Task& task,
    acl::UID uid) const
{
    if (isAllowedToModify(task, uid)) {
        return operations;
    }
    if (isAcquiredByUser(task, uid)) {
        return {sf::TaskOperation::Release};
    }
    return {};
}

} // namespace maps::wiki::acl_utils
