#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/actions/revert_commit.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/actions/save_object.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/commit_helpers.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/controller_helpers.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/extended_editor_fixture.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/object_helpers.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/objects_creator/objects_creator.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/printers.h>
#include <maps/wikimap/mapspro/services/editor/src/tests/helpers/task_helpers.h>

#include <maps/wikimap/mapspro/services/editor/src/observers/social.h>
#include <maps/wikimap/mapspro/services/editor/src/observers/view_syncronizer.h>

#include <yandex/maps/wiki/common/robot.h>
#include <yandex/maps/wiki/revision/exception.h>
#include <yandex/maps/wiki/unittest/arcadia.h>

#include <library/cpp/iterator/cartesian_product.h>
#include <library/cpp/testing/unittest/registar.h>

namespace maps::wiki::tests {

using DbWithAoi = ExtendedEditorFixture<ViewSyncronizer, SocialObserver>;


Y_UNIT_TEST_SUITE(revert)
{
    WIKI_FIXTURE_TEST_CASE_P(
        should_revert_draft_commit_for_autoapprove_category, DbWithAoi,
        CartesianProduct(ALL_REVERT_MODES, TRUSTED_USERS))
    {
        const auto aoiCreate = saveObject(user, aoi(), observers);

        const auto [revertMode, userType] = getParam();
        const auto reverter = userTypeToUid.at(userType);
        const auto result = revert(revertMode, reverter, aoiCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({aoiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({aoiCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(aoiCreate.objectId, Deleted);
        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, reverter, Revert, common::ROBOT_UID, Revert);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, common::ROBOT_UID, Revert, reverter, Revert);
                break;
        }

        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_revert_approved_commit_for_autoapprove_category, DbWithAoi, TRUSTED_USERS)
    {
        const auto aoiCreate = saveObject(user, aoi(), observers);

        performAndValidateJson<CommitsApprove>(cartographer, aoiCreate.commitId);
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, PREAPPROVING, Approved);

        const auto result = revert(RevertMode::Commit, userTypeToUid.at(getParam()), aoiCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({aoiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({aoiCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, PREAPPROVING, Approved);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(aoiCreate.objectId, Deleted);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, common::ROBOT_UID, Accept, cartographer, Approve);
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE_P(
        should_revert_directly_depending_draft_commits_for_autoapprove_category, DbWithAoi,
        CartesianProduct(ALL_REVERT_MODES, TRUSTED_USERS))
    {
        const auto aoiCreate = saveObject(user, aoi(), observers);
        const auto aoiEdit1 = saveObject(
            user,
            aoi().revisionId(aoiCreate.revisionId).attr("aoi:name", "name 1"),
            observers);
        const auto aoiEdit2 = saveObject(
            user,
            aoi().revisionId(aoiEdit1.revisionId).attr("aoi:name", "name 2"),
            observers);

        const auto [revertMode, userType] = getParam();
        const auto reverter = userTypeToUid.at(userType);
        const auto result = revert(revertMode, reverter, aoiCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({aoiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(
            result.revertedCommitIds,
            TCommitIds({aoiCreate.commitId, aoiEdit1.commitId, aoiEdit2.commitId}));
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(aoiEdit1.commitId,  DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(aoiEdit2.commitId,  DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(aoiCreate.objectId, Deleted);

        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, reverter, Revert, common::ROBOT_UID, Revert);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, common::ROBOT_UID, Revert, reverter, Revert);
                break;
        }
        WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiEdit1.commitId, common::ROBOT_UID, Revert, common::ROBOT_UID, Revert);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiEdit2.commitId, common::ROBOT_UID, Revert, common::ROBOT_UID, Revert);
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_revert_directly_depending_approved_commits, DbWithAoi, TRUSTED_USERS)
    {
        const auto bldCreate = saveObject(user, building(), observers);
        const auto bldEdit1 = saveObject(
            user,
            building().revisionId(bldCreate.revisionId).attr("bld:height", "3"),
            observers);
        const auto bldEdit2 = saveObject(
            user,
            building().revisionId(bldEdit1.revisionId).attr("bld:height", "5"),
            observers);

        performAndValidateJson<CommitsApprove>(cartographer, bldEdit1.commitId);
        performAndValidateJson<CommitsApprove>(cartographer, bldEdit2.commitId);

        const auto reverter = userTypeToUid.at(getParam());
        const auto result = revert(RevertMode::Commit, reverter, bldCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({bldCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(
            result.revertedCommitIds,
            TCommitIds({bldCreate.commitId, bldEdit1.commitId, bldEdit2.commitId}));
        WIKI_ASSERT_COMMIT_STATE(bldCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(bldEdit1.commitId, PREAPPROVING, Approved);
        WIKI_ASSERT_COMMIT_STATE(bldEdit2.commitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(bldCreate.objectId, Deleted);

        switch (getParam()) {
            case UserType::Moderator:
                WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, DRAFT, Pending);
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(bldCreate.commitId, reverter, Revert);
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
                break;
            case UserType::Cartographer:
                WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
                WIKI_ASSERT_COMMIT_TASK_CLOSED(bldCreate.commitId, reverter, Revert, common::ROBOT_UID, Revert);
                WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
                break;
            default: UNIT_ASSERT(false);
        }
        WIKI_ASSERT_COMMIT_TASK_CLOSED(bldEdit1.commitId, common::ROBOT_UID, Accept, cartographer, Approve);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(bldEdit2.commitId, common::ROBOT_UID, Accept, cartographer, Approve);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_delete_slave_objects_on_revert_by_moderator, DbWithAoi, ALL_REVERT_MODES)
    {
        const auto rdElCreate = saveObject(user, rdEl(), observers);

        const auto revertMode = getParam();
        const auto result = revert(revertMode, moderator, rdElCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({rdElCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({rdElCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(rdElCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, DRAFT, Pending);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.objectId,                    Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("start").objectId, Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("end").objectId,   Deleted);

        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(rdElCreate.commitId, moderator, Revert);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(rdElCreate.commitId, common::ROBOT_UID, Revert, moderator, Revert);
                break;
        }
        WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_delete_slave_objects_on_revert_by_cartographer, DbWithAoi, ALL_REVERT_MODES)
    {
        const auto rdElCreate = saveObject(user, rdEl(), observers);

        const auto revertMode = getParam();
        const auto result = revert(revertMode, cartographer, rdElCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({rdElCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({rdElCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(rdElCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.objectId,                    Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("start").objectId, Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("end").objectId,   Deleted);

        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(rdElCreate.commitId, cartographer, Revert, common::ROBOT_UID, Revert);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(rdElCreate.commitId, common::ROBOT_UID, Revert, cartographer, Revert);
                break;
        }
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_revert_indirectly_depending_commits, DbWithAoi, ALL_REVERT_MODES)
    {
        const auto rdElCreate = saveObject(user, rdEl(), observers);
        auto startRdJc = rdElCreate.slaves.at("start");
        auto startRdJcNewGeom = startRdJc.geometry;
        std::get<Point>(startRdJcNewGeom).x += 0.01;

        const auto rdJcEdit = saveObject(
            user,
            rdJc().revisionId(startRdJc.revisionId).geometry(startRdJcNewGeom),
            observers);

        const auto revertMode = getParam();
        const auto result = revert(revertMode, moderator, rdElCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({rdElCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({rdElCreate.commitId, rdJcEdit.commitId}));
        WIKI_ASSERT_COMMIT_STATE(rdElCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(rdJcEdit.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, DRAFT, Pending);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.objectId,                    Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("start").objectId, Deleted);
        WIKI_ASSERT_OBJECT_STATE(rdElCreate.slaves.at("end").objectId,   Deleted);

        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(rdElCreate.commitId, moderator, Revert);
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(rdJcEdit.commitId, common::ROBOT_UID, Revert);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(rdElCreate.commitId, common::ROBOT_UID, Revert, moderator, Revert);
                WIKI_ASSERT_COMMIT_TASK_CLOSED(rdJcEdit.commitId, common::ROBOT_UID, Revert, common::ROBOT_UID, Revert);
                break;
        }
        WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_revert_reverting_commit, DbWithAoi, TRUSTED_USERS)
    {
        const auto bldCreate = saveObject(user, building(), observers);

        const auto bldCreateRevert = revert(RevertMode::Commit, moderator, bldCreate.commitId);
        WIKI_ASSERT_COMMIT_STATE(bldCreateRevert.revertCommitId, DRAFT, Pending);

        const auto userType = getParam();
        const auto result = revert(RevertMode::Commit, userTypeToUid.at(userType), bldCreateRevert.revertCommitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({bldCreateRevert.revertCommitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({bldCreateRevert.revertCommitId}));
        WIKI_ASSERT_COMMIT_STATE(bldCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_OBJECT_STATE(bldCreate.objectId, Draft);
        WIKI_ASSERT_COMMIT_TASK_RESOLVED(bldCreate.commitId, moderator, Revert);

        switch (userType) {
            case UserType::Moderator:
                WIKI_ASSERT_COMMIT_STATE(bldCreateRevert.revertCommitId, DRAFT, Pending);
                WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, DRAFT, Pending);
                WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
                break;
            case UserType::Cartographer:
                WIKI_ASSERT_COMMIT_STATE(bldCreateRevert.revertCommitId, DRAFT, Rejected);
                WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
                WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
                break;
            default: UNIT_ASSERT(false);
        }
    }

    WIKI_FIXTURE_TEST_CASE_P(should_not_revert_already_reverted_commit, DbWithAoi, TRUSTED_USERS)
    {
        const auto create = saveObject(user, building(), observers);
        revert(RevertMode::Commit, moderator, create.commitId);
        UNIT_ASSERT_EXCEPTION(
            revert(RevertMode::Commit, userTypeToUid.at(getParam()), create.commitId),
            revision::AlreadyRevertedDirectlyCommitsException
        );

        // It is impossible to try to revert this commit from the moderation
        // console, because it can't be acquired.
    }

    WIKI_FIXTURE_TEST_CASE(should_not_revert_already_indirectly_reverted_commit, DbWithAoi)
    {
        const auto rdElCreate = saveObject(user, rdEl(), observers);
        auto startRdJc = rdElCreate.slaves.at("start");
        auto startRdJcNewGeom = startRdJc.geometry;
        std::get<Point>(startRdJcNewGeom).x += 0.01;

        const auto rdJcEdit = saveObject(
            user,
            rdJc().revisionId(startRdJc.revisionId).geometry(startRdJcNewGeom),
            observers);

        revert(RevertMode::Commit, moderator, rdElCreate.commitId);
        UNIT_ASSERT_EXCEPTION(
            revert(RevertMode::Commit, cartographer, rdJcEdit.commitId),
            revision::AlreadyRevertedCommitsException
        );
        UNIT_ASSERT_EXCEPTION(
            revert(RevertMode::Resolve, cartographer, rdJcEdit.commitId),
            LogicException
        );
        UNIT_ASSERT_EXCEPTION(
            revert(RevertMode::Close, cartographer, rdJcEdit.commitId),
            revision::AlreadyRevertedCommitsException
        );
    }

    WIKI_FIXTURE_TEST_CASE_P(should_revert_accepted_task_for_autoapprove_category, DbWithAoi, TRUSTED_USERS)
    {
        const auto aoiCreate = saveObject(user, aoi(), observers);

        const auto taskId = acquire(moderator, aoiCreate.commitId);
        performRequest<SocialModerationTasksResolve>(
            observers, UserContext(moderator, {}), defaultAoiId, social::ResolveResolution::Accept, social::TaskIds({taskId})
        );
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, PREAPPROVING, Approved);

        const auto result = revert(RevertMode::Commit, userTypeToUid.at(getParam()), aoiCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({aoiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({aoiCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, PREAPPROVING, Approved);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(aoiCreate.objectId, Deleted);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(aoiCreate.commitId, moderator, Accept, common::ROBOT_UID, Approve); // The task has been autoapproved
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE(should_revert_accepted_task_for_non_autoapprove_category, DbWithAoi)
    {
        const auto poiCreate = saveObject(user, poiFood(), observers);

        const auto taskId = acquire(moderator, poiCreate.commitId);
        performRequest<SocialModerationTasksResolve>(
            observers, UserContext(moderator, {}), defaultAoiId, social::ResolveResolution::Accept, social::TaskIds({taskId})
        );
        WIKI_ASSERT_COMMIT_STATE(poiCreate.commitId, DRAFT, Pending);

        const auto result = revert(RevertMode::Commit, cartographer, poiCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({poiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({poiCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(poiCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(poiCreate.objectId, Deleted);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(poiCreate.commitId, moderator, Accept, common::ROBOT_UID, Revert);
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE(should_revert_closed_task, DbWithAoi)
    {
        const auto bldCreate = saveObject(user, building(), observers);

        // @todo Lock task prio to closing
        performRequest<SocialModerationTasksClose>(
            observers,
            UserContext(cartographer, {}),
            defaultAoiId,
            social::CloseResolution::Approve,
            social::TaskIds{getTask(bldCreate.commitId)->id()}
        );

        const auto result = revert(RevertMode::Commit, cartographer, bldCreate.commitId);
        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({bldCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({bldCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(bldCreate.commitId, PREAPPROVING, Approved);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, PREAPPROVING, Approved);
        WIKI_ASSERT_OBJECT_STATE(bldCreate.objectId, Deleted);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(bldCreate.commitId, common::ROBOT_UID, Accept, cartographer, Approve);
        WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
    }

    WIKI_FIXTURE_TEST_CASE(should_close_reverted_task_for_non_autoapprove_category, DbWithAoi)
    {
        const auto poiCreate = saveObject(user, poiFood(), observers);
        const auto result = revert(RevertMode::Commit, moderator, poiCreate.commitId);

        const auto taskId = acquire(cartographer, poiCreate.commitId);
        performRequest<SocialModerationTasksClose>(
            observers,
            UserContext(cartographer, {}),
            defaultAoiId,
            social::CloseResolution::Approve,
            social::TaskIds{taskId}
        );

        UNIT_ASSERT_VALUES_EQUAL(result.revertedDirectlyCommitIds, TCommitIds({poiCreate.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(result.revertedCommitIds, TCommitIds({poiCreate.commitId}));
        WIKI_ASSERT_COMMIT_STATE(poiCreate.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(result.revertCommitId, DRAFT, Pending);
        WIKI_ASSERT_OBJECT_STATE(poiCreate.objectId, Deleted);
        WIKI_ASSERT_COMMIT_TASK_CLOSED(poiCreate.commitId, moderator, Revert, cartographer, Approve);
        WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
    }

    WIKI_FIXTURE_TEST_CASE_P(should_approve_contributing_commits_when_reverted_one_is_closed, DbWithAoi, ALL_REVERT_MODES)
    {
        const auto poiCreate = saveObject(user, poiFood(), observers);
        const auto poiEdit = saveObject(
            user,
            poiFood().revisionId(poiCreate.revisionId).attr("poi_food:disp_class", "10"),
            observers);

        const auto revertMode = getParam();
        const auto poiEditRevert = revert(revertMode, moderator, poiEdit.commitId);

        const auto taskId = acquire(cartographer, poiEditRevert.revertCommitId);
        performRequest<SocialModerationTasksClose>(
            observers,
            UserContext(cartographer, {}),
            defaultAoiId,
            social::CloseResolution::Approve,
            social::TaskIds{taskId}
        );

        UNIT_ASSERT_VALUES_EQUAL(poiEditRevert.revertedDirectlyCommitIds, TCommitIds({poiEdit.commitId}));
        UNIT_ASSERT_VALUES_EQUAL(poiEditRevert.revertedCommitIds, TCommitIds({poiEdit.commitId}));

        WIKI_ASSERT_COMMIT_STATE(poiCreate.commitId, DRAFT, Approved);
        WIKI_ASSERT_COMMIT_STATE(poiEdit.commitId, DRAFT, Rejected);
        WIKI_ASSERT_COMMIT_STATE(poiEditRevert.revertCommitId, PREAPPROVING, Approved);

        WIKI_ASSERT_OBJECT_STATE(poiCreate.objectId, Draft);

        WIKI_ASSERT_COMMIT_TASK_CLOSED(poiCreate.commitId, common::ROBOT_UID, Accept, common::ROBOT_UID, Approve);

        switch (revertMode) {
            case RevertMode::Commit:
            case RevertMode::Resolve:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(poiEdit.commitId, moderator, Revert, common::ROBOT_UID, Approve);
                break;
            case RevertMode::Close:
                WIKI_ASSERT_COMMIT_TASK_CLOSED(poiEdit.commitId, common::ROBOT_UID, Revert, moderator, Revert);
                break;
        }
        WIKI_ASSERT_COMMIT_TASK_CLOSED(poiEditRevert.revertCommitId, common::ROBOT_UID, Accept, cartographer, Approve);
    }

    WIKI_FIXTURE_TEST_CASE_P(
        should_not_change_contributing_commit, DbWithAoi,
        CartesianProduct(ALL_REVERT_MODES, TRUSTED_USERS))
    {
        const auto [revertMode, userType] = getParam();

        {   // Autoapprove category
            const auto aoiCreate = saveObject(user, aoi(), observers);
            const auto aoiEdit = saveObject(
                user,
                aoi().revisionId(aoiCreate.revisionId).attr("aoi:name", "name"),
                observers);

            const auto result = revert(revertMode, userTypeToUid.at(userType), aoiEdit.commitId);
            WIKI_ASSERT_COMMIT_STATE(aoiCreate.commitId, DRAFT, Pending);
            WIKI_ASSERT_OBJECT_STATE(aoiCreate.objectId, Draft);
            WIKI_ASSERT_COMMIT_TASK_UNRESOLVED(aoiCreate.commitId);
            WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
        }

        {   // Non-autoapprove category
            const auto poiCreate = saveObject(user, poiFood(), observers);
            const auto poiEdit = saveObject(
                user,
                poiFood().revisionId(poiCreate.revisionId).attr("poi_food:disp_class", "10"),
                observers);

            const auto result = revert(revertMode, userTypeToUid.at(userType), poiEdit.commitId);
            WIKI_ASSERT_COMMIT_STATE(poiCreate.commitId, DRAFT, Pending);
            WIKI_ASSERT_OBJECT_STATE(poiCreate.objectId, Draft);
            WIKI_ASSERT_COMMIT_TASK_UNRESOLVED(poiCreate.commitId);

            switch (userType) {
                case UserType::Moderator:
                    WIKI_ASSERT_COMMIT_TASK_RESOLVED(result.revertCommitId, common::ROBOT_UID, Accept);
                    break;
                case UserType::Cartographer:
                    WIKI_ASSERT_COMMIT_HAS_NO_TASK(result.revertCommitId);
                    break;
                default: UNIT_ASSERT(false);
            }
        }
    }
}

} // namespace maps::wiki::tests
