#include "moderation.h"

#include <maps/wikimap/mapspro/services/tasks_social/src/assessment_sampler/lib/common.h>

#include <maps/wikimap/mapspro/libs/common/include/yandex/maps/wiki/common/batch.h>
#include <maps/wikimap/mapspro/libs/common/include/yandex/maps/wiki/common/robot.h>

#include <fmt/format.h>


namespace maps::wiki::assessment::sampler {

namespace {

using namespace fmt::literals;

const std::string RESOLVE = "resolve";
const std::string CLOSE = "close";

constexpr size_t TASK_SELECTION_BATCH_SIZE = 100;

social::Tasks collectModerationTasks(
    social::Gateway& gateway,
    const social::TaskFilter& filter)
{
    social::Tasks tasks;

    common::applyBatchOp(
        gateway.getTaskIds(filter),
        TASK_SELECTION_BATCH_SIZE,
        [&gateway, &tasks](const TIds& taskIds) {
            auto batch = gateway.loadTasksByTaskIds(taskIds);
            std::move(batch.begin(), batch.end(), std::back_inserter(tasks));
        });
    return tasks;
}

GroupNameToUnits loadModerationResolveUnits(
    social::Gateway& gateway,
    social::DateTimeCondition resolvedAt,
    const TUids& allowedUids)
{
    social::TaskFilter filter;
    filter.setResolvedBy(allowedUids);
    filter.setResolvedAt(resolvedAt);

    auto tasks = collectModerationTasks(gateway, filter);

    GroupNameToUnits result;
    for (const auto& task : tasks) {
        if (common::isRobot(task.resolved().uid())) {
            continue;
        }

        const auto resolvedBy = task.resolved().uid();
        const auto categoryId = task.event().getPrimaryObjectCategory();

        const std::string groupName = fmt::format(
            "{category}-{resolved_by}",
            "category"_a = categoryId ? *categoryId : COMMON,
            "resolved_by"_a = resolvedBy
        );

        result[groupName].emplace_back(Unit{
            .id = UNKNOWN_UNIT_ID,
            .entity = {
                .id = std::to_string(task.id()),
                .domain = Entity::Domain::Moderation
            },
            .action = {
                .name = RESOLVE,
                .by = resolvedBy,
                .at = chrono::parseSqlDateTime(task.resolved().date())
            }
        });
    }
    return result;
}

GroupNameToUnits loadModerationCloseUnits(
    social::Gateway& gateway,
    const social::DateTimeCondition& closedAt,
    const TUids& allowedUids)
{
    social::TaskFilter filter;
    filter.setClosedBy(allowedUids);
    filter.setClosedAt(closedAt);

    auto tasks = collectModerationTasks(gateway, filter);

    GroupNameToUnits result;
    for (const auto& task : tasks) {
        if (common::isRobot(task.closed().uid())) {
            continue;
        }

        const auto closedBy = task.closed().uid();
        const auto categoryId = task.event().getPrimaryObjectCategory();

        const std::string groupName = fmt::format(
            "{category}-{closed_by}",
            "category"_a = categoryId ? *categoryId : COMMON,
            "closed_by"_a = closedBy
        );

        result[groupName].emplace_back(Unit{
            .id = UNKNOWN_UNIT_ID,
            .entity = {
                .id = std::to_string(task.id()),
                .domain = Entity::Domain::Moderation
            },
            .action = {
                .name = CLOSE,
                .by = closedBy,
                .at = chrono::parseSqlDateTime(task.closed().date())
            }
        });
    }
    return result;
}

} // namespace

GroupNameToUnits loadModerationUnits(
    social::Gateway& socialGw,
    chrono::TimePoint timepointMin,
    chrono::TimePoint timepointMax,
    const TUids& allowedUids)
{
    social::DateTimeCondition timeCondition(timepointMin, timepointMax);

    GroupNameToUnits result;
    result = loadModerationResolveUnits(socialGw, timeCondition, allowedUids);
    result += loadModerationCloseUnits(socialGw, timeCondition, allowedUids);
    return result;
}

} // maps::wiki::assessment::sampler
