#include <maps/wikimap/mapspro/services/tasks_social/src/notifications_dispatcher/lib/published_commits_dispatcher.h>

#include <yandex/maps/wiki/tasks/status_writer.h>
#include <yandex/maps/wiki/common/default_config.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>
#include <yandex/maps/wiki/common/pg_advisory_lock_ids.h>
#include <yandex/maps/wiki/common/string_utils.h>

#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>
#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/file_utils.h>
#include <maps/libs/json/include/value.h>

#include <library/cpp/resource/resource.h>

#include <vector>
#include <cstdint>
#include <string>

namespace common = maps::wiki::common;
namespace nd = maps::wiki::notifications_dispatcher;

std::vector<int64_t> readWhitelist() {
    auto content = NResource::Find("/whitelist.txt");
    auto value = maps::json::Value::fromString(content);

    std::vector<int64_t> result;
    result.reserve(value.size());
    for (const auto& val : value) {
        result.push_back(val["uid"].as<int64_t>());
    }
    return result;
}

int main(int argc, char** argv) try {
    maps::cmdline::Parser parser;

    auto useWhitelist = parser.flag("whitelist")
        .help("Filter users with predifined whitelist");

    auto configPath = parser.file("config")
        .help("Path to services config");
    auto syslogTag = parser.string("syslog-tag")
        .help("Redirect log output to syslog with given tag");
    auto statusDir = parser.string("status-dir")
        .help("path to status dir");

    parser.parse(argc, argv);

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

    std::unique_ptr<common::ExtendedXmlDoc> configDocPtr;
    if (configPath.defined()) {
        configDocPtr = std::make_unique<common::ExtendedXmlDoc>(configPath);
    } else {
        configDocPtr = common::loadDefaultConfig();
    }
    common::PoolHolder socialPoolHolder(*configDocPtr, "social", "grinder");

    maps::pgp3utils::PgAdvisoryXactMutex guard(
        socialPoolHolder.pool(),
        static_cast<int64_t>(common::AdvisoryLockIds::NOTIFICATIONS_DISPATCHER));
    if (!guard.try_lock()) {
        INFO() << "Notifications dispatcher is already running (DB is locked)";
        return 0;
    }

    common::PoolHolder corePoolHolder(*configDocPtr, "core", "grinder");

    std::optional<std::string> statusFileName;
    if (statusDir.defined()) {
        statusFileName = statusDir + "/wiki-notifications-dispatcher.status";
    }
    maps::wiki::tasks::StatusWriter statusWriter(std::move(statusFileName));

    std::vector<int64_t> whitelist;
    if (useWhitelist) {
        whitelist = readWhitelist();
    }

    INFO() << "Notifications dispatcher started";

    try {
        nd::PublishedCommitsDispatcher dispatcher(
            *configDocPtr,
            corePoolHolder.pool(),
            socialPoolHolder.pool(),
            whitelist);
        dispatcher.updateNotificationData();
        dispatcher.sendNotifications();

        statusWriter.flush();
    } catch (const std::exception& e) {
        statusWriter.err(e.what());
        statusWriter.flush();
        throw;
    }

    INFO() << "Notifications dispatcher finished";
    return EXIT_SUCCESS;
} catch (const maps::Exception& ex) {
    FATAL() << "Notifications dispatcher failed: " << ex;
    return EXIT_FAILURE;
} catch (const std::exception& ex) {
    FATAL() << "Notifications dispatcher failed: " << ex.what();
    return EXIT_FAILURE;
}
