#include "commit_merger.h"
#include "sql_strings.h"
#include "branch_manager_impl.h"
#include "helpers.h"

#include <yandex/maps/wiki/revision/exception.h>

#include <boost/format.hpp>

namespace maps::wiki::revision {

using namespace helpers;

namespace {

const boost::format FORMAT_SELECT_APPROVED_TO_STABLE(
    "SELECT " + sql::col::ID + " FROM " + sql::table::COMMIT +
    " WHERE " + sql::col::STATE + "='" + sql::val::COMMIT_STATE_APPROVED + "'"
        " AND " + sql::col::STABLE_BRANCH_ID + " IS NULL"
        " AND " + sql::col::ID + "<=%1%"
    " ORDER BY " + sql::col::ID + " FOR UPDATE");

const boost::format FORMAT_UPDATE_APPROVED_TO_STABLE(
    "UPDATE " + sql::table::COMMIT +
    " SET " + sql::col::STABLE_BRANCH_ID + "=%1%"
    " WHERE " + sql::col::STATE + "='" + sql::val::COMMIT_STATE_APPROVED + "'"
        " AND " + sql::col::STABLE_BRANCH_ID + " IS NULL"
        " AND " + sql::col::ID + "<=%2%");

} // namespace


CommitMerger::CommitMerger(Transaction& work)
    : work_(work)
{}

DBIDSet
CommitMerger::lockApprovedCommits(DBID maxCommitId) const
{
    auto r = work_.exec(
        (boost::format(FORMAT_SELECT_APPROVED_TO_STABLE) % maxCommitId).str());

    DBIDSet commitIds;
    for (size_t i = 0; i < r.size(); ++i) {
        commitIds.insert(r[i][0].as<DBID>());
    }
    return commitIds;
}

DBIDSet
CommitMerger::approvedToStable(DBID maxCommitId) const
{
    TransactionGuard guard(work_);

    auto branchData = BranchManagerImpl(work_).loadByType(BranchType::Stable, true);
    if (branchData.state == BranchState::Progress) {
        throw BranchInProgressException() <<
            "forbidden merging approved commits into stable branch"
            ", id: " << branchData.id;
    }

    auto commitIds = lockApprovedCommits(maxCommitId);
    if (!commitIds.empty()) {
        auto r = work_.exec(
            (boost::format(FORMAT_UPDATE_APPROVED_TO_STABLE)
                % branchData.id % maxCommitId).str());
        REQUIRE(r.affected_rows() == commitIds.size(),
            "wrong updated commit records: " <<
            r.affected_rows() << " != " << commitIds.size());
    }

    guard.ok();
    return commitIds;
}

} // namespace maps::wiki::revision
