#include "db_access.h"

#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/wiki/common/retry_duration.h>
#include <yandex/maps/wiki/common/revision_utils.h>
#include <yandex/maps/wiki/revision/revisionsgateway.h>

namespace maps {
namespace wiki {
namespace diffalert {
namespace {

bool viewSupportedBranch(const revision::Branch& branch)
{
    return branch.type() == revision::BranchType::Stable ||
           branch.type() == revision::BranchType::Archive;
}

void logFunc(const std::string& msg)
{ INFO() << msg; }

std::string getViewSchemaForBranch(uint64_t branchId)
{
    if (branchId == revision::TRUNK_BRANCH_ID) {
        return "vrevisions_trunk";
    }
    return "vrevisions_stable_" + std::to_string(branchId);
}

bool doesSchemaExist(pqxx::transaction_base& txn, const std::string& schema)
{
    return txn.exec(
        "SELECT EXISTS ("
            "SELECT * FROM pg_namespace WHERE nspname = " + txn.quote(schema) +
         ')')[0][0].as<bool>();
}

} // namespace

RevSnapshotFactory::RevSnapshotFactory(
        revision::Branch branch,
        revision::SnapshotId snapshotId,
        pgpool3::Pool& pool)
    : branch_(std::move(branch))
    , snapshotId_(std::move(snapshotId))
    , pool_(pool)
{ }

RevSnapshotHolder RevSnapshotFactory::get()
{
    auto txn = common::getReadTransactionForCommit(
        pool_, branch_.id(), snapshotId_.commitId(), logFunc);
    revision::RevisionsGateway gateway(*txn, branch_);
    return {std::move(txn), gateway.snapshot(snapshotId_)};
}

ViewTxnFactory::ViewTxnFactory(
        revision::Branch branch,
        pgpool3::Pool& pool)
    : branch_(std::move(branch))
    , schemaName_(getViewSchemaForBranch(branch_.id()))
    , pool_(pool)
    , isAvailable_(false)
{
    if (!viewSupportedBranch(branch_)) {
        return;
    }

    common::retryDuration([&] {
        auto txn = pool_.masterWriteableTransaction();
        token_ = pgpool3::generateToken(*txn);
        isAvailable_ = doesSchemaExist(*txn, schemaName_);
        txn->commit();
    });
}

bool ViewTxnFactory::isAvailable() const
{
    return isAvailable_;
}

pgpool3::TransactionHandle ViewTxnFactory::get()
{
    REQUIRE(isAvailable_, "view is not supported for branch " << branch_.id());

    auto txn = pool_.slaveTransaction(token_);
    REQUIRE(doesSchemaExist(*txn, schemaName_), "missing schema " << schemaName_);
    txn->exec("SET search_path = " + schemaName_ + ", public");
    return txn;
}

} // namespace diffalert
} // namespace wiki
} // namespace maps
