#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/log8/include/log8.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/generate_absent_house_number/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_absent_traffic_light/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_lane_hypothesis/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_traffic_sign/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_wrong_condition/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_wrong_direction/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_wrong_parking/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/generate_wrong_speed_limit/include/generator.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/wiki_config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <util/generic/yexception.h>

#include <chrono>
#include <string>

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


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 wikiConfigDirPath = parser.string("wiki-config-dir")
            .defaultValue(maps::mrc::common::WIKI_TEMPLATE_CONFIG_DIR)
            .help("path to services config template for wikimap");

    auto wikiConfigPath = parser.string("wiki-config")
            .help("path to services config for wikimap");

    auto objectIdsPath = parser.string("object-ids")
            .help("path to object ids file");

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

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

    auto concurrency = parser.num("concurrency")
            .defaultValue(4)
            .help("number of parallel validations");

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

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

    auto timeout = parser.num("timeout")
            .help("Time period between sequential runs (default 5)")
            .defaultValue(5);

    auto validationType = parser.string("validate")
            .help("Type of validation")
            .required();

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

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

    const auto mrcConfig =
        maps::mrc::common::templateConfigFromCmdPath(secretVersion, mrcConfigPath);
    auto wikiConfig = wikiConfigPath.defined()
        ? maps::wiki::common::ExtendedXmlDoc(wikiConfigPath)
        : maps::mrc::common::makeWikiConfig(wikiConfigDirPath);

    auto mrcPoolHolder = mrcConfig.makePoolHolder();
    maps::wiki::common::PoolHolder wikiPoolHolder{wikiConfig, "long-read", "mrc-eye"};

    std::unique_ptr<BaseMrcWorker> generator;

    HypothesisGeneratorConfig config;
    config.mrc.pool = &mrcPoolHolder.pool();
    config.mrc.commit = commit;
    config.mrc.lockFree = lockFree;
    config.wiki.pool = &wikiPoolHolder.pool();
    config.wiki.concurrency = concurrency;;

    if ("lane-hypothesis" == validationType) {
        generator.reset(new LaneHypothesisGenerator(config));
    } else if ("absent-traffic-light" == validationType) {
        generator.reset(new AbsentTrafficLightGenerator(config));
    } else if ("absent-house-number" == validationType) {
        generator.reset(new AbsentHouseNumberGenerator(config));
    } else if ("traffic-sign" == validationType) {
        generator.reset(new TrafficSignGenerator(config));
    } else if ("wrong-speed-limit" == validationType) {
        generator.reset(new WrongSpeedLimitGenerator(config));
    } else if ("wrong-direction" == validationType) {
        generator.reset(new WrongDirectionGenerator(config));
    } else if ("wrong-condition" == validationType) {
        generator.reset(new WrongConditionGenerator(config));
    } else if ("wrong-parking" == validationType) {
        generator.reset(new WrongParkingGenerator(config));
    } else {
        throw maps::RuntimeError() << "Unknown validation type: " << validationType;
    }

    if (not loop) {
        if (objectIdsPath.defined()) {
            for (IdStream idStream(objectIdsPath); idStream.valid(); ) {
                generator->processBatch(idStream.readBatch(batchSize));
            }
        } else {
            WARN() << "No object ids!";
        }

        return EXIT_SUCCESS;
    }

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

    generator->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;
}
