#include <maps/libs/chrono/include/days.h>
#include <maps/libs/chrono/include/time_point.h>
#include <maps/wikimap/mapspro/libs/configs_editor/include/yandex/maps/wiki/configs/editor/category_groups.h>
#include <maps/wikimap/mapspro/libs/configs_editor/include/yandex/maps/wiki/configs/editor/config_holder.h>
#include <maps/wikimap/mapspro/libs/social/include/yandex/maps/wiki/social/event.h>
#include <maps/wikimap/mapspro/libs/social/include/yandex/maps/wiki/social/gateway.h>
#include <maps/wikimap/mapspro/libs/social/tests/helpers/event_creator.h>
#include <maps/wikimap/mapspro/libs/social/tests/helpers/task_creator.h>
#include <maps/wikimap/mapspro/libs/unittest/include/yandex/maps/wiki/unittest/unittest.h>
#include <maps/wikimap/mapspro/services/tasks_social/src/assessment_sampler/tests/helpers.h>

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

namespace feedback = maps::wiki::social::feedback;

std::pair<chrono::TimePoint, chrono::TimePoint> txnNowSpan(pqxx::transaction_base& txn)
{
    return chrono::getComprisingTimeSpan<chrono::Days>(unittest::txnNow(txn));
}

UnitVec generateUnits(size_t size, TUid uid)
{
    UnitVec result;
    result.reserve(size);
    static size_t entityId = 1;

    for (; result.size() < size; ++entityId) {
        Unit unit = {
            .id = 0,
            .entity = {
                .id = std::to_string(entityId),
                .domain = GENERATED_ENTITY_DOMAIN
            },
            .action = {
                .name = ACTION_ACCEPT,
                .by = uid,
                .at = DATE
            }
        };
        result.emplace_back(std::move(unit));
    }
    return result;
}

namespace {

bool unitsLess(const Unit& lhs, const Unit& rhs)
{
    return lhs.entity < rhs.entity || lhs.entity == rhs.entity && lhs.action < rhs.action;
}

} // namespace

void sortUnits(UnitVec& units)
{
    std::sort(units.begin(), units.end(), unitsLess);
}

UnitVec unitsSetDifference(const UnitVec& lhs, const UnitVec& rhs)
{
    UnitVec minuend{lhs};
    UnitVec subtrahend{rhs};
    UnitVec difference;

    sortUnits(minuend);
    sortUnits(subtrahend);

    std::set_difference(
        minuend.cbegin(), minuend.cend(),
        subtrahend.cbegin(), subtrahend.cend(),
        std::back_inserter(difference),
        unitsLess);

    return difference;
}


social::Task createNewTask(pqxx::transaction_base& txn)
{
    return social::tests::TaskCreator(txn);
}


social::Task createResolvedTask(
    pqxx::transaction_base& txn,
    TUid resolvedBy,
    chrono::TimePoint resolvedAt)
{
    return social::tests::TaskCreator(txn)
        .resolved(resolvedBy, social::ResolveResolution::Revert, txn.quote(chrono::formatSqlDateTime(resolvedAt)));
}


social::Task createClosedTask(
    pqxx::transaction_base& txn,
    TUid resolvedBy,
    chrono::TimePoint resolvedAt,
    TUid closedBy,
    chrono::TimePoint closedAt)
{
    return social::tests::TaskCreator(txn)
        .resolved(resolvedBy, social::ResolveResolution::Accept, txn.quote(chrono::formatSqlDateTime(resolvedAt)))
        .closed(closedBy, social::CloseResolution::Edit, txn.quote(chrono::formatSqlDateTime(closedAt)));
}


TId createEditCommitEvent(
    pqxx::transaction_base& txn,
    TUid createdBy,
    chrono::TimePoint createdAt,
    const std::string& action,
    const std::optional<std::string>& primaryObjectCategoryId,
    maps::wiki::revision::DBID branchId)
{
    social::tests::EventCreator creator(txn);

    creator.uid(createdBy).action(action).branchId(branchId);
    if (primaryObjectCategoryId) {
        creator.primaryObjData({TId(1), *primaryObjectCategoryId, "label", "edit-notes"});
    }

    const auto event = social::Event(creator);
    txn.exec(
        "UPDATE social.commit_event "
        "SET created_at = '" + chrono::formatSqlDateTime(createdAt) + "' "
        "WHERE event_id = " + std::to_string(event.id()));
    return event.commitData()->commitId();
}

configs::editor::StringSet serviceGroupCategories()
{
    return configs::editor::ConfigHolder()
        .categoryGroups()
        .categoryIdsByGroup("service_group");
}

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