#pragma once

#include <yandex/maps/wiki/social/event_filter.h>
#include <yandex/maps/wiki/social/moderation_time_intervals.h>
#include <yandex/maps/wiki/social/task.h>

#include <yandex/maps/wiki/common/string_utils.h>

#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <pqxx/pqxx>

namespace maps::wiki::social::tasks {

struct LockedTaskIds
{
    TaskIds resolved;
    TaskIds notResolved;
};

LockedTaskIds
selectForUpdateTasksByTaskIds(
    pqxx::transaction_base& work,
    const TaskIds& taskIds);

LockedTaskIds
selectForUpdateEditTasksByCommitIds(
    pqxx::transaction_base& work,
    const TIds& commitIds);

TaskIds
commitIdsToEditTaskIds(
    pqxx::transaction_base& work,
    const TIds& commitIds,
    size_t batchSize);

TaskIds loadTaskIds(const pqxx::result& rows);

template <typename FilterParam>
std::string filterParamClause(
    const std::optional<FilterParam>& param,
    const std::string& tableAlias,
    const std::string& columnName,
    pqxx::transaction_base& work)
{
    if (!param) {
        return "";
    }
    return " AND " + tableAlias + columnName +
                    "=" + work.quote(boost::lexical_cast<std::string>(*param));
}

template <typename FilterParam>
std::string filterParamClause(
    const std::optional<std::set<FilterParam>>& setParam,
    const std::string& tableAlias,
    const std::string& columnName,
    pqxx::transaction_base& work)
{
    if (!setParam || setParam->empty()) {
        return "";
    }

    return " AND " + tableAlias + columnName + " IN ("
        + common::join(
            *setParam,
            [&](const auto& param) {
                if constexpr (std::is_arithmetic_v<FilterParam>) {
                    return param;
                } else {
                    return work.quote(param);
                }
            },
            ',')
        + ")";
}

std::string
primaryObjectCategoryIdClause(
    const EventFilter& filter,
    const std::string& tableAlias,
    pqxx::transaction_base& work);

std::string
suspiciousUsersClause(
    const std::optional<bool>& suspiciousUsers,
    const std::string& commitEventTableAlias);

std::string noviceUsersClause (
    const std::optional<bool>& noviceUsers,
    const std::string& taskTableAlias);

std::string
commitEventTaskClause(
    std::optional<ModerationMode> mode,
    const std::string& taskAlias,
    const ModerationTimeIntervals& moderationTimeIntervals);

std::string
feedbackEventTaskClause(const std::string& taskAlias);

std::string aoiTaskActiveClause(std::optional<TId> aoiId, const std::string& taskAlias);

std::string nonLockedClause();
std::string lockedClause(TUid uid);
std::string lockedByOthersClause(TUid uid);

std::string oldClause(
    ModerationMode mode,
    const std::string& taskAlias,
    const ModerationTimeIntervals& moderationTimeIntervals);

std::string acquirableClause(TUid uid);

std::string deferredClause(
    std::optional<Deferred> deferred,
    TUid uid,
    const std::string& taskAlias);

size_t oldTaskAgeInHours(
    ModerationMode mode,
    const ModerationTimeIntervals& moderationTimeIntervals);

template <typename Resolution, typename TIds>
TaskIds
selectAndUpdateTasks(
    pqxx::transaction_base& work,
    TUid uid,
    Resolution resolution,
    const TIds& taskOrCommitIds,
    const boost::format& selectQuery,
    const boost::format& updateQuery)
{
    if (taskOrCommitIds.empty()) {
        return {};
    }

    auto rows = work.exec(str(boost::format(selectQuery)
        % uid
        % common::join(taskOrCommitIds, ',')));
    if (rows.empty()) {
        return {};
    }

    TaskIds taskIds = loadTaskIds(rows);
    work.exec(str(boost::format(updateQuery)
        % resolution
        % uid
        % common::join(taskIds, ',')));
    return taskIds;
}

} // namespace maps::wiki::social::tasks
