#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_panel/include/detect_panel.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/id_stream.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/load.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/secure_config.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/libs/cmdline/include/cmdline.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.h>

#include <util/generic/yexception.h>

#include <chrono>
#include <string>

using namespace maps;
using namespace maps::mrc;
using namespace maps::mrc::eye;

namespace {

maps::mrc::yt::PoolType parseYtPoolType(const std::string& value)
{
    if (value == "ad_hoc") {
        return maps::mrc::yt::PoolType::AdHoc;
    } else if (value == "processing") {
        return maps::mrc::yt::PoolType::Processing;
    } else {
       throw maps::RuntimeError() << "Unsupported ytPoolType value '" << value << "'";
    }
}

} // namespace

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

    maps::cmdline::Parser parser;

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

    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 frameIdsPath = parser.string("frame-ids")
            .help("path to frame ids file");

    auto loop = parser.flag("loop")
            .help("work in infinite loop");

    auto batchSize = parser.num("batch")
            .defaultValue(1000)
            .help("frame batch size (default 1000)");

    auto margin = parser.num("margin")
            .defaultValue(3)
            .help("cold start margin");

    auto partitionSize = parser.num("partition-size")
            .defaultValue(70)
            .help("partition size");

    auto commit = parser.flag("commit")
            .help("commit results");

    auto ytCommit = parser.flag("yt-commit")
            .help("commit yt results (for debug)");

    auto concurrency = parser.num("concurrency")
            .defaultValue(100)
            .help("number of parallel yt jobs");

    auto ytPath = parser.string("yt-path")
            .help("yt root path (get from config by default)");

    auto ytPoolType = parser.string("yt-pool-type")
            .defaultValue("processing")
            .help("yt pool type ('ad_hoc' or 'processing')");

    auto lockFree = parser.flag("lock-free")
            .help("don't take pg lock (may be dangerous)");

    auto timeout = parser.num("timeout")
            .help("wait for db update in minutes (default 5)")
            .defaultValue(5);

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

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

    const auto config =
        maps::mrc::common::templateConfigFromCmdPath(secretVersion,
                                                     mrcConfigPath);
    maps::mrc::eye::SecureConfig::initialize(config);

    auto poolHolder = config.makePoolHolder();
    const auto loader = FrameLoader::fromConfig(config);

    const auto yt = config.externals().yt().makeClient();

    DetectPanelConfig workerConfig;
    workerConfig.yt.client = yt.Get();
    workerConfig.yt.concurrency = concurrency;
    workerConfig.yt.frameLoader = &loader;
    workerConfig.yt.commit = ytCommit;
    workerConfig.yt.rootPath = ytPath.defined() ? ytPath : config.externals().yt().path() + "/detect_panel";
    workerConfig.yt.partitionSize = partitionSize;
    workerConfig.yt.poolType = parseYtPoolType(ytPoolType);
    workerConfig.mrc.pool = &poolHolder.pool();
    workerConfig.mrc.commit = commit;
    workerConfig.mrc.lockFree = lockFree;
    workerConfig.margin = margin;

    DetectPanel detect(workerConfig);

    if (not loop) {
        if (frameIdsPath.defined()) {
            for (IdStream idStream(frameIdsPath); idStream.valid(); ) {
                detect.processBatch(idStream.readBatch(batchSize));
            }
        } else {
            WARN() << "No feature ids!";
        }

        return EXIT_SUCCESS;
    }

    INFO() << "Infinite loop mode...";
    if (frameIdsPath.defined()) {
        WARN() << "Ignore file '" << frameIdsPath << "'";
    }

    detect.runInLoopMode(batchSize, std::chrono::minutes(timeout));

    return EXIT_SUCCESS;
} catch (const maps::Exception& e) {
    FATAL() << e;
    return EXIT_FAILURE;
} catch (const yexception& e) {
    FATAL() << e.what();

    if (e.BackTrace()) {
        FATAL() << e.BackTrace()->PrintToString();
    }

    return EXIT_FAILURE;
} catch (const std::exception& e) {
    FATAL() << e.what();
    return EXIT_FAILURE;
} catch (...) {
    FATAL() << "Unknown error!";
    return EXIT_FAILURE;
}
