#include <yandex/maps/wiki/common/pg_retry_helpers.h>

#include <maps/libs/common/include/exception.h>
#include <yandex/maps/wiki/common/retry_duration.h>

#include <maps/libs/log8/include/log8.h>

namespace maps::wiki::common {

namespace {

const auto MAX_EXECUTION_ATTEMPTS = 2;
const std::string SET_SEARCH_PATH_PUBLIC = "SET search_path=public";

const std::string& getSearchPath(const std::string& searchPath)
{
    return searchPath.empty()
        ? SET_SEARCH_PATH_PUBLIC
        : searchPath;
}

} // namespace

void execCommitWithRetries(
    pgpool3::Pool& pool,
    const std::string& name,
    const std::string& searchPath,
    const std::string& query,
    const std::function<void(pqxx::transaction_base&)>& f)
{
    size_t retryCounter = 0;
    size_t execCounter = 0;

    common::retryDuration([&] {
        if (retryCounter++) {
            INFO() << "Attempt " << retryCounter << " " << name;
        }

        try {
            auto conn = pool.getMasterConnection();
            pqxx::work work(conn.get());
            work.exec(getSearchPath(searchPath)); // test connectivity to database

            ++execCounter;
            if (!query.empty()) {
                work.exec(query);
            }
            if (f) {
                f(work);
            }
            work.commit();
        } catch (const std::exception& ex) {
            ERROR() << "Attempt " << retryCounter << " " << name
                    << " failed: " << ex.what();
            if (execCounter < MAX_EXECUTION_ATTEMPTS) {
                // may be fail connectivity on lost connection, exec(), commit()...
                throw;
            }
            throw common::RetryDurationCancel() << ex.what(); // data error
        }
    });
}

} // namespace maps::wiki::common
