#include "result_handler.h"

#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/threadpool_wrapper.h>
#include <maps/wikimap/mapspro/services/mrc/libs/ugc_event_logger/include/logger.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/environment.h>
#include <maps/libs/common/include/exception.h>

#include <chrono>
#include <functional>

namespace queue = maps::mrc::signal_queue;
namespace up = maps::mrc::uploader;

namespace {

constexpr std::chrono::seconds QUEUE_POLLING_INTERVAL{1};
const auto THREADS_NUM = std::max(1u, std::thread::hardware_concurrency() / 4u);

std::atomic<bool> g_stop;

} // anonymous namespace

namespace maps {
namespace mrc {
namespace uploader {

void handleSignal(int /*sig*/) { g_stop.store(true); }

void run(const maps::mrc::common::Config& cfg, const std::string& ugcEventLogPath)
{
    INFO() << "Starting to upload results";

    for (int sig : {SIGTERM, SIGINT, SIGQUIT}) {
        std::signal(sig, handleSignal);
    }

    const std::chrono::seconds LOG_ROTATION_PERIOD(3600);
    maps::mrc::ugc_event_logger::Logger ugcEventLogger(ugcEventLogPath, LOG_ROTATION_PERIOD);
    maps::mrc::uploader::ResultHandler resultHandler(cfg, ugcEventLogger);

    // Thread pool's queue size is limited to mitigate irreversible data
    // losses in case of a crash.
    common::ThreadpoolWrapper threadPool{THREADS_NUM, 2 /* queueSize */};

    while (!g_stop.load()) {
        auto variants
            = resultHandler.pop<queue::AssignmentImage,
                                queue::RideImage>();

        for (auto&& variant : variants) {
            std::visit([&](auto&& result) {
                threadPool->add([&, result = std::move(result)] {
                    resultHandler.process(std::move(result));
                });
            }, variant);
        }

        // If all result queues are empty then take a nap.
        if (variants.empty()) {
            std::this_thread::sleep_for(QUEUE_POLLING_INTERVAL);
        }

        threadPool.checkExceptions();
    }

    INFO() << "Service is shutting down";
}

} //namespace uploader
} //namespace mrc
} //namespace maps

int main(int argc, char* argv[]) try {

    maps::cmdline::Parser parser;
    auto configPath = parser.string("config")
        .help("path to override the configuration");

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

    auto ugcEventLogPath = parser.string("ugc-event-log")
        .help("path to ugc events log")
        .defaultValue("/var/log/yandex/maps/mrc/ugc_events_uploader/ugc_events.log");

    parser.parse(argc, argv);

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

    auto config = maps::mrc::common::templateConfigFromCmdPath(configPath);

    switch (maps::common::getYandexEnvironment()) {
        case maps::common::Environment::Stable:
        case maps::common::Environment::Testing:
            INFO() << "Enable TVM suppport";
            config.enableTvmClient();
            break;
        default:
            break;
    }

    up::run(config, ugcEventLogPath);

    return EXIT_SUCCESS;
}
catch (const maps::Exception& e) {
    ERROR() << e;
    return EXIT_FAILURE;
}
catch (const std::exception& e) {
    ERROR() << e.what();
    return EXIT_FAILURE;
}
