#include "config.h"

#include "build_patch_feed.h"
#include "common.h"

#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>
#include <yandex/maps/wiki/common/extended_xml_doc.h>
#include <yandex/maps/wiki/common/default_config.h>
#include <yandex/maps/wiki/common/pg_advisory_lock_ids.h>

#include <yandex/maps/wiki/tasks/status_writer.h>

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

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/chrono/include/time_point.h>

#include <filesystem>

using maps::wiki::common::ExtendedXmlDoc;

namespace fs = std::filesystem;
namespace merge_poi = maps::wiki::merge_poi;

namespace {

merge_poi::MergeTaskId
createMergeTaskId()
{
    auto now = maps::chrono::TimePoint::clock::now();
    return now.time_since_epoch().count();
}

void runMergePoi(
    const ExtendedXmlDoc& configXml,
    const std::optional<std::string>& statusFilename)
{
    using namespace maps::wiki;

    maps::wiki::tasks::StatusWriter statusWriter(statusFilename);
    try {
        merge_poi::Config cfg {configXml};

        // Exclusive execution beetween hosts
        //
        maps::pgp3utils::PgAdvisoryXactMutex dbLocker(
            cfg.socialPool(),
            static_cast<int64_t>(maps::wiki::common::AdvisoryLockIds::MERGE_POI));
        if (!dbLocker.try_lock()) {
            INFO() << "Database is already locked. Merge POI task interrupted.";
            return;
        }

        cfg.initTvm();

        INFO() << "Editor backend:" << cfg.editorUrl();
        auto mergeTaskId = createMergeTaskId();
        INFO() << "Merge task id: " << mergeTaskId;
        INFO() << "Build patches";
        merge_poi::buildPatchFeed(cfg, mergeTaskId);
        INFO() << "Task finished";
        statusWriter.flush();
    } catch (const std::exception& e) {
        ERROR() << "Task failed: " << e.what();
        statusWriter.err(e.what());
        statusWriter.flush();
        throw;
    }
}

} // anonymous namespace

int main(int argc, char* argv[])
{
    maps::cmdline::Parser parser;
    auto workerConfig = parser.string("config")
        .help("path to the worker configuration");
    auto syslogTag = parser.string("syslog-tag")
        .help("redirect log output to syslog with given tag");
    auto statusDir = parser.option<fs::path>("status-dir")
        .help("path to status dir");

    parser.parse(argc, argv);

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

    std::optional<std::string> statusFilename;
    if (statusDir.defined()) {
        fs::path filepath(statusDir);
        filepath /= "wiki-merge-poi-worker.status";
        statusFilename = filepath;
    }

    try {
        std::unique_ptr<ExtendedXmlDoc> configDocPtr;
        if (workerConfig.defined()) {
            configDocPtr = std::make_unique<ExtendedXmlDoc>(workerConfig);
        } else {
            configDocPtr = maps::wiki::common::loadDefaultConfig();
        }

        runMergePoi(*configDocPtr, statusFilename);
        return EXIT_SUCCESS;
    } catch (const maps::Exception& e) {
        WARN() << e;

    } catch (const std::exception& e) {
        WARN() << e.what();
    }
    return EXIT_FAILURE;
}
