#include "tasks_acquire.h"
#include "helpers.h"
#include <maps/wikimap/mapspro/services/editor/src/branch_helpers.h>
#include <maps/wikimap/mapspro/services/editor/src/moderation.h>
#include <maps/wikimap/mapspro/services/editor/src/configs/config.h>
#include <maps/wikimap/mapspro/services/editor/src/exception.h>
#include <maps/wikimap/mapspro/services/editor/src/social_utils.h>

#include <yandex/maps/wiki/configs/editor/category_groups.h>
#include <maps/wikimap/mapspro/libs/acl/include/aclgateway.h>
#include <yandex/maps/wiki/social/gateway.h>

#include <chrono>

namespace maps {
namespace wiki {

namespace {

const std::string TASK_METHOD_NAME = "SocialModerationTasksAcquire";

} // namespace

SocialModerationTasksAcquire::Request::Request(
    TUid uid,
    TOid aoi,
    social::ModerationMode mode,
    const boost::optional<std::string>& categoryGroup,
    const boost::optional<maps::wiki::social::EventType>& eventType,
    const boost::optional<TUid>& createdBy,
    const boost::optional<TUid>& resolvedBy,
    const boost::optional<TOid>& primaryObjectId,
    size_t limit,
    social::TasksOrder tasksOrder,
    SocialModerationTasksAcquire::Request::SuspiciousOnly suspiciousOnly,
    SocialModerationTasksAcquire::Request::NovicesOnly novicesOnly
)
    : user(uid)
    , aoiId(aoi)
    , mode(mode)
    , categoryGroup(categoryGroup)
    , eventType(eventType)
    , createdBy(createdBy)
    , resolvedBy(resolvedBy)
    , primaryObjectId(primaryObjectId)
    , limit(limit)
    , tasksOrder(tasksOrder)
    , suspiciousOnly(suspiciousOnly)
    , novicesOnly(novicesOnly)
{
    CHECK_REQUEST_PARAM(uid);
    CHECK_REQUEST_PARAM(aoi);
    CHECK_REQUEST_PARAM(limit);

    if (categoryGroup) {
        const auto& groupId = *categoryGroup;
        const auto& groups = cfg()->editor()->categoryGroups();
        WIKI_REQUIRE(
            groups.isGroupExists(groupId) || groupId == CATEGORY_GROUP_COMMON,
            ERR_BAD_REQUEST, "unknown category group: " << groupId);
    }
}

SocialModerationTasksAcquire::Request::Request(
        TUid uid,
        TOid aoi,
        social::ModerationMode mode,
        size_t limit,
        social::TasksOrder tasksOrder)
    : user(uid)
    , aoiId(aoi)
    , mode(mode)
    , limit(limit)
    , tasksOrder(tasksOrder)
{
    CHECK_REQUEST_PARAM(uid);
    CHECK_REQUEST_PARAM(aoi);
    CHECK_REQUEST_PARAM(limit);
}

std::string
SocialModerationTasksAcquire::Request::dump() const
{
    std::stringstream ss;
    ss << " uid: " << user
       << " aoi: " << aoiId
       << " mode, acl role: " << moderation::toAclRoleName(mode);
    if (categoryGroup) {
        ss << " category-group: " << *categoryGroup;
    }
    if (eventType) {
        ss << " event-type: " << *eventType;
    }
    if (createdBy) {
        ss << " created-by: " << *createdBy;
    }
    if (resolvedBy) {
        ss << " resolved-by: " << *resolvedBy;
    }
    if (primaryObjectId) {
        ss << " primary-oid: " << *primaryObjectId;
    }
    if (suspiciousOnly == SuspiciousOnly::Yes) {
        ss << " suspicious-users: true";
    }
    if (novicesOnly == NovicesOnly::Yes) {
        ss << " novices-users: true";
    }

    ss << " limit: " << limit;
    return ss.str();
}


SocialModerationTasksAcquire::SocialModerationTasksAcquire(
        const ObserverCollection&,
        const Request& request,
        taskutils::TaskID asyncTaskID)
    : controller::BaseController<SocialModerationTasksAcquire>(BOOST_CURRENT_FUNCTION, asyncTaskID)
    , request_(request)
{}

std::string
SocialModerationTasksAcquire::printRequest() const
{
    return request_.dump();
}

void
SocialModerationTasksAcquire::control()
{
    auto branchCtx = CheckedTrunkBranchContextFacade().acquireWrite();
    auto permissionsChecker = CheckPermissions(request_.user, branchCtx.txnCore());

    acl::ACLGateway aclGateway(branchCtx.txnCore());
    auto user = aclGateway.user(request_.user);
    user.checkActiveStatus();
    auto aoi = aclGateway.aoi(request_.aoiId);
    if (aoi.deleted() == acl::Deleted::Yes) {
        return;
    }
    const auto regionPolicies =
        moderation::createRegionPolicies(user, aoi.id(), request_.mode);
    if (regionPolicies.empty()) {
        return;
    }
    ASSERT(regionPolicies.size() == 1);

    social::Gateway socialGw(branchCtx.txnSocial());
    auto modConsole = socialGw.moderationConsole(request_.user);

    social::EventFilter eventFilter;
    eventFilter.moderationMode(request_.mode);
    eventFilter.aoiId(request_.aoiId);
    eventFilter.deferred(social::Deferred::No);
    if (request_.eventType) {
        eventFilter.eventType(*request_.eventType);
    }
    if (request_.categoryGroup) {
        if (*request_.categoryGroup != CATEGORY_GROUP_COMMON) {
            const auto& groupId = *request_.categoryGroup;
            const auto& groups = cfg()->editor()->categoryGroups();
            eventFilter.categoryIds(groups.categoryIdsByGroup(groupId));
            eventFilter.commonTasksPermitted(false);
        } else {
            // Common tasks are not belong to any specific category,
            // so all tasks with category groups must be filtered out.
            eventFilter.categoryIds({});
            eventFilter.commonTasksPermitted(true);
        }
    } else {
        const auto& groupIds =
            regionPolicies.front().categoryGroupIds;
        const auto& groups = cfg()->editor()->categoryGroups();
        eventFilter.categoryIds(groups.categoryIdsByGroups(groupIds));
        eventFilter.commonTasksPermitted(areCommonTasksPermitted(regionPolicies));
    }
    if (request_.createdBy) {
        eventFilter.createdBy(*request_.createdBy);
    }
    if (request_.resolvedBy) {
        eventFilter.resolvedBy(*request_.resolvedBy);
    }
    if (request_.primaryObjectId) {
        eventFilter.objectId(*request_.primaryObjectId);
    }
    if (request_.suspiciousOnly == Request::SuspiciousOnly::Yes) {
        eventFilter.suspiciousUsers(true);
    }
    if (request_.novicesOnly == Request::NovicesOnly::Yes) {
        eventFilter.noviceUsers(true);
    }

    result_->tasks = modConsole.acquireTasks(
        eventFilter, request_.limit, request_.tasksOrder, cfg()->moderationTimeIntervals());
    result_->alertsByTaskId = fillAlertsByTaskId(
        result_->tasks, socialGw, permissionsChecker);
    result_->token = branchCtx.commit();
}

const std::string&
SocialModerationTasksAcquire::taskName()
{
    return TASK_METHOD_NAME;
}

} // namespace wiki
} // namespace maps
