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

#include <yandex/maps/wiki/revision/exception.h>
#include <yandex/maps/wiki/revision/branch_manager.h>
#include <boost/format.hpp>

namespace maps::wiki::revision {

using namespace helpers;

namespace {

const boost::format FORMAT_CREATE_BRANCH(
    "INSERT INTO " + sql::table::BRANCH +
        " (" + sql::col::TYPE + "," + sql::col::CREATED_BY + "," + sql::col::ATTRIBUTES + ")"
        " VALUES ('%1%', %2%, %3%) "
        " RETURNING " + BRANCH_COLUMNS);

const std::string SELECT_BRANCH_PREFIX =
    "SELECT " + BRANCH_COLUMNS + " FROM " + sql::table::BRANCH;

const std::string BRANCH_LOCK_POSTFIX = " FOR UPDATE"; // without NOWAIT


BranchData
createExistedBranchData(const pqxx::result& r)
{
    if (r.empty()) {
        throw BranchNotExistsException() << "branch not exists";
    }
    ASSERT(r.size() == 1);
    return BranchData(r[0]);
}

} // namespace


BranchManagerImpl::BranchManagerImpl(pqxx::transaction_base& work)
    : work_(work)
{}

BranchData
BranchManagerImpl::create(BranchType type, UserID createdBy, const Attributes& attributes) const
{
    TransactionGuard guard(work_);

    Hstore hstore = attributesToHstore(work_, attributes);

    auto r = work_.exec(
        (boost::format(FORMAT_CREATE_BRANCH)
            % type
            % createdBy
            % (hstore.empty() ? sql::val::UNDEFINED : hstore)).str());

    REQUIRE(!r.empty(), "can not create stable branch by " << createdBy);
    ASSERT(r.size() == 1);

    BranchData data(r[0]);

    guard.ok();
    return data;
}

BranchData
BranchManagerImpl::loadByType(BranchType type, bool lock) const
{
    std::ostringstream query;
    query << SELECT_BRANCH_PREFIX << " WHERE " << sql::col::TYPE << "='" << type << "'";
    if (lock) {
        query << BRANCH_LOCK_POSTFIX;
    }

    return createExistedBranchData(work_.exec(query.str()));
}

BranchData
BranchManagerImpl::loadById(DBID id, bool lock) const
{
    std::ostringstream query;
    query << SELECT_BRANCH_PREFIX << " WHERE " << sql::col::ID << "=" << id;
    if (lock) {
        query << BRANCH_LOCK_POSTFIX;
    }

    return createExistedBranchData(work_.exec(query.str()));
}

} // namespace maps::wiki::revision
