#include "worker.h"
#include "common.h"
#include "mapreduce/yt/interface/client.h"
#include "maps/wikimap/mapspro/services/mrc/libs/config/include/config.h"

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/pg_locks.h>

#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>

#include <chrono>

namespace maps::mrc::import_taxi {

namespace {

NYT::IClientPtr createYtClient(const common::Config& cfg)
{
    const auto& importCfg = cfg.taxiImportConfig();

    return NYT::CreateClient(
        TString(importCfg.ytCluster()),
        NYT::TCreateClientOptions().Token(TString(importCfg.ytToken()))
    );
}

} // namespace

Worker::Worker(
    common::Config cfg,
    pgpool3::Pool& pool,
    const std::string& graphFolder,
    const std::string& geoIdPath,
    bool dryRun
)
    : cfg_(std::move(cfg))
    , pool_(pool)
    , ytClient_(createYtClient(cfg_))
    , importer_(pool_, cfg_.makeMdsClient(), {}, makeTrackProvider(), graphFolder, geoIdPath, dryRun)
    , dryRun_(dryRun)
{}

void Worker::runOnce(pqxx::transaction_base& /*lockTxn*/)
{
    INFO() << "Start import";

    auto ytTxn = ytClient_->StartTransaction();
    importer_.setEventsFeed(makeTaxiEventsFeed(ytTxn));
    importer_.import();
}

void Worker::run()
{
    if (dryRun_) {
        WARN() << "Dry run is ON";
    }

    constexpr std::chrono::seconds SLEEP_INTERVAL{60};

    while (true) {
        try {
            pgp3utils::PgAdvisoryXactMutex locker(
                pool_,
                static_cast<int64_t>(common::LockId::ImportTaxi));
            if (!dryRun_ && !locker.try_lock()) {
                INFO() << "Another process is ongoing";
            } else {
                runOnce(locker.writableTxn());
            }
        } catch (const maps::Exception& e) {
            ERROR() << "Caught exception: " << e;
        } catch (const std::exception& e) {
            ERROR() << "Caught exception: " << e.what();
        }

        INFO() << "Sleep for " << SLEEP_INTERVAL.count() << "s till the next run";
        std::this_thread::sleep_for(SLEEP_INTERVAL);
    }
}

std::shared_ptr<VideoEventsFeed> Worker::makeTaxiEventsFeed(
    NYT::IClientBasePtr ytClient)
{
    auto metadata = getMetadata(pool_);

    INFO() << "Max processed event id = " << metadata.maxProcessedEventId;

    return std::make_shared<TaxiVideoEventsFeed>(
        ytClient,
        cfg_.taxiImportConfig().ytEventsPath(),
        metadata.maxProcessedEventId);
}

std::unique_ptr<ITrackProvider> Worker::makeTrackProvider()
{
    return std::make_unique<TrackProvider>(
        ytClient_,
        cfg_.taxiImportConfig().ytTracksPath(),
        cfg_.externals().yt().path() + "/import_taxi_preloaded_tracks");
}

} // namespace maps::mrc::import_taxi
