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

#include <maps/wikimap/mapspro/services/mrc/libs/blackbox_client/include/blackbox_client.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/pg_locks.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/yt/include/io.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/regular_upload/lib/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/regular_upload/lib/include/uploader.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/regular_upload/lib/include/tools.h>

#include <maps/libs/common/include/exception.h>
#include <maps/libs/common/include/profiletimer.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>

#include <string>

using namespace maps::mrc::regular_upload;

namespace {

constexpr size_t BATCH_SIZE = 10000;

} // namespace

int main(int argc, const char** argv) try {
    NYT::Initialize(argc, argv);

    maps::cmdline::Parser parser;

    auto mrcConfigPath = parser.string("mrc-config")
        .help("path to mrc config");

    auto secretVersion = parser.string("secret-version")
        .help("version for secrets from yav.yandex-team.ru");

    auto featuresTableName = parser.string("features-table")
        .help("Output YT table name");

    auto syslog = parser.string("syslog-tag")
        .help("redirect log output to syslog with given tag");

    parser.parse(argc, const_cast<char**>(argv));

    if (syslog.defined()) {
        maps::log8::setBackend(maps::log8::toSyslog(syslog));
    }

    const auto mrcConfig =
        maps::mrc::common::templateConfigFromCmdPath(secretVersion,
                                                     mrcConfigPath);

    const auto tablePath = featuresTableName.defined()
        ? featuresTableName
        : mrcConfig.externals().yt().path() + "/features_export";

    auto poolHolder =
        mrcConfig.makePoolHolder(maps::mrc::common::LONG_READ_DB_ID,
                                 maps::mrc::common::LONG_READ_POOL_ID);
    const auto mrcMds = mrcConfig.makeMdsClient();
    const auto ytClient = mrcConfig.externals().yt().makeClient();
    const auto blackboxClientHolder =
        std::make_unique<maps::mrc::blackbox_client::BlackboxClient>(
            maps::auth::TvmtoolSettings());

    const auto regularUploadConfig = RegularUploadConfig()
        .setMds(&mrcMds)
        .setPool(&poolHolder.pool())
        .setYtClient(ytClient.Get())
        .setBlackboxClient(blackboxClientHolder.get())
        .setFeaturesTablePath(tablePath)
        .setUploadBatchSize(BATCH_SIZE);

    maps::pgp3utils::PgAdvisoryXactMutex mutex(
        *regularUploadConfig.pool(),
        static_cast<int64_t>(maps::mrc::common::LockId::RegularUpload));
    if (!mutex.try_lock()) {
        INFO() << "Another process is ongoing";
        return EXIT_SUCCESS;
    }

    ProfileTimer timer;
    INFO() << "Starting...";
    RegularUploader uploader(regularUploadConfig);
    uint64_t totalLoadedFeatures = uploader.loadAndSaveFromDatabase();

    setLastRunTime(regularUploadConfig.pool()->masterWriteableTransaction());
    INFO() << "Finished!";
    INFO() << "Total loaded features: " << totalLoadedFeatures;
    INFO() << "Total elapsed: " << timer.getElapsedTime() << "s";

    return EXIT_SUCCESS;
} catch (const maps::Exception& ex) {
    FATAL() << "Failed: " << ex;
    return EXIT_FAILURE;
} catch (const std::exception& ex) {
    FATAL() << "Failed: " << ex.what();
    return EXIT_FAILURE;
} catch (const yexception& e) {
    FATAL() << e.what();
    if (e.BackTrace()) {
        FATAL() << e.BackTrace()->PrintToString();
    }
    return EXIT_FAILURE;
}
