#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/libs/common/include/pg_locks.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/ride_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/ride_inspector/lib/context.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/ride_inspector/lib/hypotheses.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/ride_inspector/lib/process_queue.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>

namespace ride_inspector = maps::mrc::ride_inspector;

/// for each new hypothesis tries to establish a relation with the ride
static void processHypothesesQueue(maps::pgpool3::Pool& pool)
{
    using namespace maps::mrc;
    while (ride_inspector::hypothesesQueuePop(
        pool, [](maps::sql_chemistry::Transaction& txn, db::TId hypothesisId) {
            auto rideHypothesis =
                ride_inspector::tryMakeRideHypothesis(txn, hypothesisId);
            if (rideHypothesis && !db::RideHypothesisGateway{txn}.exists(
                                      db::table::RideHypothesis::rideId ==
                                          rideHypothesis->rideId() &&
                                      db::table::RideHypothesis::hypothesisId ==
                                          rideHypothesis->hypothesisId())) {
                db::RideHypothesisGateway{txn}.insert(*rideHypothesis);
                db::RideGateway{txn}.updatex(
                    db::RideGateway{txn}.loadById(rideHypothesis->rideId()));
            }
        }))
        ;
}

int main(int argc, char* argv[]) try {
    // clang-format off
    maps::cmdline::Parser parser(
        "Tool calculates the positions of the photos and publishes them."
    );
    // clang-format on
    auto syslog = parser.string("syslog-tag")
        .help("redirect log output to syslog with given tag");
    auto configPath = parser.string("config").help("path to configuration");
    parser.parse(argc, argv);

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

    auto cfg = maps::mrc::common::templateConfigFromCmdPath(configPath);
    auto ctx = ride_inspector::Context{cfg};

    maps::pgp3utils::PgAdvisoryXactMutex mutex(
        ctx.pool(),
        static_cast<int64_t>(maps::mrc::common::LockId::RideInspector));
    if (!mutex.try_lock()) {
        INFO() << "Another process is ongoing";
        return EXIT_SUCCESS;
    }

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