#include "tasks_close.h"

#include <maps/wikimap/mapspro/services/editor/src/approved_commits/approver.h>
#include <maps/wikimap/mapspro/services/editor/src/branch_helpers.h>
#include <maps/wikimap/mapspro/services/editor/src/exception.h>
#include <maps/wikimap/mapspro/services/editor/src/moderation.h>
#include <maps/wikimap/mapspro/services/editor/src/moderation_log.h>
#include <maps/wikimap/mapspro/services/editor/src/actions/commits/reverter.h>
#include "helpers.h"

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

#include <maps/wikimap/mapspro/libs/acl/include/aclgateway.h>
#include <yandex/maps/wiki/common/robot.h>
#include <yandex/maps/wiki/common/string_utils.h>
#include <yandex/maps/wiki/revision/commit.h>
#include <yandex/maps/wiki/social/gateway.h>

namespace maps::wiki {

namespace {
const std::string TASK_METHOD_NAME = "SocialModerationTasksClose";
}


SocialModerationTasksClose::SocialModerationTasksClose(
        const ObserverCollection& observers,
        const Request& request,
        taskutils::TaskID asyncTaskID)
    : SocialModerationCommentTasksProcessor<SocialModerationTasksClose, social::CloseResolution>(observers, request, asyncTaskID)
{}

void
SocialModerationTasksClose::control()
{
    auto branchCtx = BranchContextFacade::acquireWrite(
        revision::TRUNK_BRANCH_ID, request_.userId());

    const auto& user = request_.userContext.aclUser(branchCtx);
    user.checkActiveStatus();

    const auto& moderationStatus = request_.userContext.moderationStatus(branchCtx);

    social::Gateway socialGateway(branchCtx.txnSocial());
    auto tasks = moderation::loadTasks(
        socialGateway,
        request_.taskIds,
        request_.userId(),
        moderation::canResolveLockedByOther(acl_utils::isCartographer(moderationStatus))
    );
    moderation::checkTasks(tasks);

    if (moderation::hasComments(tasks)) {
        processComments(branchCtx, tasks);
    } else if (moderation::hasFeedbackTasks(tasks)) {
        processFeedbackAccept(socialGateway, tasks);
    } else {
        if (request_.resolution == social::CloseResolution::Revert) {
            processRevert(branchCtx, socialGateway, tasks);
        } else {
            processClose(branchCtx, socialGateway, user, tasks);
        }
    }

    if (result_->token.empty()) {
        result_->token = branchCtx.commit();
    }

    logModerationEvent(result_->taskIds, ModerationLogger::Action::Closed);
}

void
SocialModerationTasksClose::processClose(
    BranchContext& branchCtx,
    const social::Gateway& socialGw,
    const acl::User& user,
    const social::Tasks& tasks) const
{
    auto superModConsole = socialGw.superModerationConsole(user.uid());

    result_->taskIds = superModConsole.closeRevertedTasks(
        tasks,
        request_.resolution
    );

    // Non revert tasks are all tasks but closed ones.
    social::Tasks nonRevertTasks;
    for (const auto& task: tasks) {
        if (!result_->taskIds.count(task.id())) {
            nonRevertTasks.emplace_back(task);
        }
    }

    const auto nonRevertCommitIds = moderation::commitIdsFromEditTasks(nonRevertTasks);

    if (!nonRevertCommitIds.empty()) {
        approved_commits::CommitsApprover approver(branchCtx, user, nonRevertCommitIds);
        result_->taskIds.merge(approver.closeTasksWithContributing(request_.resolution));
        approver.awakeUpdater();
    }

    checkResult(request_.resolution, request_.taskIds, result_->taskIds);

    result_->token = branchCtx.commit();
}

void
SocialModerationTasksClose::processRevert(
    BranchContext& branchCtx,
    const social::Gateway& socialGateway,
    const social::Tasks& tasks)
{
    TCommitIds commitsToBeClosed{moderation::commitIdsFromEditTasks(tasks)};
    CommitsReverter reverter(observers_, branchCtx, request_.userContext);
    auto revertData = reverter.revertCommits(commitsToBeClosed, request_.revertReason);

    auto robotConsole = socialGateway.superModerationConsole(common::ROBOT_UID);
    auto userConsole = socialGateway.superModerationConsole(request_.userId());

    TCommitIds allCommits{commitsToBeClosed};
    allCommits.insert(
        revertData.revertedCommitIds.begin(),
        revertData.revertedCommitIds.end());
    robotConsole.resolveEditTasksByCommitIds(social::ResolveResolution::Revert, allCommits);

    auto closedTaskIds =
        userConsole.closeEditTasksByCommitIds(
            commitsToBeClosed, social::ResolveResolution::Revert, social::CloseResolution::Revert
        );

    using moderation::operator-;
    closedTaskIds.merge(
        robotConsole.closeEditTasksByCommitIds(
            revertData.revertedCommitIds - commitsToBeClosed,
            social::ResolveResolution::Revert, social::CloseResolution::Revert
        )
    );

    checkResult(social::CloseResolution::Revert, request_.taskIds, closedTaskIds);

    result_->token   = reverter.syncViewAndCommit(revertData);
    result_->taskIds = std::move(closedTaskIds);
    result_->revertingCommitModel = CommitModel(revertData.createdCommit);
}

void
SocialModerationTasksClose::processFeedbackAccept(
    const social::Gateway& socialGateway,
    const social::Tasks& tasks) const
{
    moderation::checkTasksType(tasks, social::EventType::ClosedFeedback);

    social::TIds taskIds;
    for (const auto& task : tasks) {
        taskIds.insert(task.id());
    }

    auto modConsole = socialGateway.superModerationConsole(request_.userId());
    result_->taskIds = modConsole.closeTasksByTaskIds(
        social::CloseResolution::Approve,
        taskIds
    );

    checkResult(social::CloseResolution::Approve, taskIds, result_->taskIds);
}

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

} // namespace maps::wiki
