#include "util.h"

#include <maps/wikimap/mapspro/libs/unittest/include/yandex/maps/wiki/unittest/unittest.h>

namespace maps::wiki::unittest {

namespace {

const std::string DB_CORE = "core";
const std::string POOL_CORE = "core";

} // namespace

DatabaseFixture::DatabaseFixture(
        const std::string& configFile,
        const std::string& tag,
        const std::string& migrationsPath)
    : configXmlPtr_(new common::ExtendedXmlDoc(configFile))
    , poolHolder_(*configXmlPtr_, DB_CORE, POOL_CORE)
    , lockTxn_(pool().masterReadOnlyTransaction())
    , lockId_(makeLockIdStr(tag))
    , migrationsPath_(migrationsPath)
{
    init();
}

DatabaseFixture::DatabaseFixture(
        const std::string& configFile,
        const std::string& dbId,
        const std::string& tag,
        const std::string& migrationsPath)
    : configXmlPtr_(new common::ExtendedXmlDoc(configFile))
    , poolHolder_(*configXmlPtr_, dbId, POOL_CORE)
    , lockTxn_(pool().masterReadOnlyTransaction())
    , lockId_(makeLockIdStr(tag))
    , migrationsPath_(migrationsPath)
{
    init();
}

DatabaseFixture::~DatabaseFixture()
{
    try {
        pgUnlock();
    } catch (...) {
        std::cerr << "Error while unlocking pg advisory lock" << std::endl;
    }
}

void DatabaseFixture::init()
{
    pgLock();

    auto conn = pool().getMasterConnection();

    dropNonSystemSchemas(conn.get());
    createPublicSchema(conn.get());
    applyMigrations(conn.get(), migrationsPath_);
}

void DatabaseFixture::pgLock()
{
    auto sql = "SELECT pg_catalog.pg_advisory_lock(" + lockId_ + ");";
    lockTxn_->exec(sql.c_str());
}

void DatabaseFixture::pgUnlock()
{
    auto sql = "SELECT pg_catalog.pg_advisory_unlock(" + lockId_ + ");";
    lockTxn_->exec(sql.c_str());
}

common::ExtendedXmlDoc& DatabaseFixture::configXml()
{
    return *configXmlPtr_;
}

common::PoolHolder& DatabaseFixture::poolHolder()
{
    return poolHolder_;
}

pgpool3::Pool& DatabaseFixture::pool()
{
    return poolHolder_.pool();
}

chrono::TimePoint txnNow(pqxx::transaction_base& txn)
{
    return chrono::parseSqlDateTime(
        txn.exec("SELECT NOW()")[0][0].as<std::string>());
}

} // maps::wiki::unittest
