#include <yandex/maps/wiki/common/default_config.h>
#include <yandex/maps/wiki/common/extended_xml_doc.h>
#include <yandex/maps/wiki/common/pg_advisory_lock_ids.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>
#include <maps/wikimap/mapspro/libs/acl/include/aclgateway.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/exception.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>

#include <chrono>
#include <thread>

namespace common = maps::wiki::common;
namespace acl = maps::wiki::acl;

using namespace std::chrono_literals;

namespace maps::wiki {

void run(
    pgpool3::Pool& corePool)
try {
    pgp3utils::PgAdvisoryXactMutex locker(
        corePool,
        static_cast<int64_t>(common::AdvisoryLockIds::ACL_CLUSTERS));
    if (!locker.try_lock()) {
        INFO() << "Database is already locked. Go on sleeping";
        return;
    }

    auto& coreWriteTxn = locker.writableTxn();
    INFO() << "Processed queue records: " <<
        acl::ACLGateway(coreWriteTxn).processClustersUpdateQueue();
    coreWriteTxn.commit();
} catch (const maps::Exception& e) {
    ERROR() << "Caught exception while processing clusters update queue: " << e;
} catch (const std::exception& e) {
    ERROR() << "Caught exception while processing clusters update queue: " << e.what();
}

} //namespace maps::wiki

int main(int argc, char* argv[])
try {
    maps::cmdline::Parser parser;
    auto configPath = parser
        .file("config")
        .help("Path to services.xml");
    auto syslogTag = parser
        .string("syslog-tag")
        .help("Redirect log output to syslog with given tag");
    parser.parse(argc, argv);
    if (syslogTag.defined()) {
        maps::log8::setBackend(maps::log8::toSyslog(syslogTag));
    }
    auto configDocPtr =
        configPath.defined()
        ? std::make_unique<common::ExtendedXmlDoc>(configPath)
        : common::loadDefaultConfig();
    common::PoolHolder coreDbHolder(*configDocPtr, "core", "grinder");
    while (true) {
        maps::wiki::run(coreDbHolder.pool());
        std::this_thread::sleep_for(1min);
    }
    return EXIT_SUCCESS;
} catch (const maps::Exception& e) {
    ERROR() << "Worker failed: " << e;
    return EXIT_FAILURE;
} catch (const std::exception& e) {
    ERROR() << "Worker failed: " << e.what();
    return EXIT_FAILURE;
}
