#include <maps/wikimap/mapspro/services/mrc/eye/lib/recognition_task/include/manager.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/id_stream.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 <chrono>
#include <thread>

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

int main(int argc, const char** argv) try {
    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 recognitionIdsPath = parser.string("recognition-ids")
            .help("path to recognition ids file");

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

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

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

    auto lockFree = parser.flag("lock-free")
            .help("lock free import (may be dangerous)");

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

    auto enableSign = parser.flag("enable-sign")
            .help("enable sign recognition");

    auto enableTrafficLight = parser.flag("enable-traffic-light")
            .help("enable traffic light recognition");

    auto enableHouseNumber = parser.flag("enable-house-number")
            .help("enable house number recognition");

    auto enableRoadMarking = parser.flag("enable-road-marking")
            .help("enable road marking recognition");

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

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

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

    wiki::common::PoolHolder poolHolder = config.makePoolHolder();
    FrameUrlResolver frameUrlResolver(
        config.externals().mrcBrowserUrl(),
        config.externals().mrcBrowserProUrl());

    db::eye::RecognitionTypes recognitionTypes;
    if (enableSign) {
        recognitionTypes.push_back(db::eye::RecognitionType::DetectSign);
    }
    if (enableTrafficLight) {
        recognitionTypes.push_back(db::eye::RecognitionType::DetectTrafficLight);
    }
    if (enableHouseNumber) {
        recognitionTypes.push_back(db::eye::RecognitionType::DetectHouseNumber);
    }
    if (enableRoadMarking) {
        recognitionTypes.push_back(db::eye::RecognitionType::DetectRoadMarking);
    }

    RecognitionTaskManagerConfig workerConfig;
    workerConfig.mrc.pool = &poolHolder.pool();
    workerConfig.mrc.commit = commit;
    workerConfig.mrc.lockFree = lockFree;
    workerConfig.frameUrlResolver = &frameUrlResolver;
    workerConfig.recognitionTypes = recognitionTypes;

    RecognitionTaskManager manager(workerConfig);

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

        return EXIT_SUCCESS;
    }

    INFO() << "Infinite loop mode...";
    if (recognitionIdsPath.defined()) {
        WARN() << "Ignore recognition ids '" << recognitionIdsPath << "'";
    }

    manager.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;
}
