#include <maps/wikimap/ugc/backoffice/src/assignments_cleaner/lib/expire.h>
#include <maps/wikimap/ugc/libs/common/constants.h>
#include <maps/wikimap/ugc/libs/common/dbqueries.h>

#include <maps/wikimap/mapspro/libs/query_builder/include/delete_query.h>
#include <maps/wikimap/mapspro/libs/query_builder/include/select_query.h>
#include <maps/wikimap/mapspro/libs/query_builder/include/update_query.h>
#include <maps/libs/log8/include/log8.h>

namespace maps::wiki::ugc::backoffice::assignments_cleaner {

namespace {

namespace qb = maps::wiki::query_builder;

const std::string NOW = "NOW() AT TIME ZONE 'UTC'";
const std::string FOUR_WEEKS_BEFORE = NOW + " - INTERVAL '28 DAYS'";
const std::string UPDATED_AT_PLUS_TTL = columns::UPDATED_AT + " AT TIME ZONE 'UTC' + " + columns::TTL;

const unsigned MAX_AFFECT_ROWS = 1000;

const auto SELECT_FOR_DELETE = qb::SelectQuery(
    tables::ASSIGNMENT,
    {columns::UID, columns::TASK_ID},
    qb::WhereConditions()
        .keyInQuotedValues(
            columns::STATUS,
            {
                std::string(toString(AssignmentStatus::Expired)),
                std::string(toString(AssignmentStatus::Skipped))
            })
        .append(
            UPDATED_AT_PLUS_TTL,
            FOUR_WEEKS_BEFORE,
            qb::Relation::LessOrEqual)
)
.orderBy(UPDATED_AT_PLUS_TTL)
.limit(MAX_AFFECT_ROWS);

const auto SELECT_FOR_UPDATE_STATUS = qb::SelectQuery(
    tables::ASSIGNMENT,
    {columns::UID, columns::TASK_ID},
    qb::WhereConditions()
        .appendQuoted(
            columns::STATUS,
            std::string(toString(AssignmentStatus::Active)))
        .append(
            UPDATED_AT_PLUS_TTL,
            NOW,
            qb::Relation::LessOrEqual)
)
.orderBy(UPDATED_AT_PLUS_TTL)
.limit(MAX_AFFECT_ROWS);

} // namespace

void expireAssignments(pqxx::transaction_base& txn)
{
    const auto result = qb::UpdateQuery(
        tables::ASSIGNMENT,
        qb::WhereConditions()
            .keyInValues(
                "(" + columns::UID + ", " + columns::TASK_ID + ")",
                {"(" + SELECT_FOR_UPDATE_STATUS.asString(txn) + ")"})
    )
    .appendQuoted(
        columns::STATUS,
        std::string(toString(AssignmentStatus::Expired)))
    .exec(txn);

    INFO() << "Set expired status for " << result.affected_rows() << " assignments";
}

void deleteAssignments(pqxx::transaction_base& txn)
{
    const auto result = qb::DeleteQuery(
        tables::ASSIGNMENT,
        qb::WhereConditions()
            .keyInValues(
                "(" + columns::UID + ", " + columns::TASK_ID + ")",
                {"(" + SELECT_FOR_DELETE.asString(txn) + ")"})
    ).exec(txn);
    INFO() << "Deleted " << result.affected_rows() << " assignments";
}

void expireAssignments(const IDbPools& pools, bool dryRun)
{
    for (size_t i = 0; i < pools.size(); i++) {
        INFO() << "Expire assignments in cluster " << i << "...";
        {
            auto txn = pools[i].masterWriteableTransaction();
            expireAssignments(*txn);
            finishTxn(*txn, dryRun);
        }
        {
            auto txn = pools[i].masterWriteableTransaction();
            deleteAssignments(*txn);
            finishTxn(*txn, dryRun);
        }
    }
}

} // namespace maps::wiki::ugc::backoffice::assignments_cleaner
