#include "maps/libs/enum_io/include/enum_io_fwd.h"
#include "toloka/platform.h"
#include <maps/wikimap/mapspro/services/mrc/long_tasks/toloka_manager_cron_jobs/lib/include/cancel_tasks.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/toloka_manager_cron_jobs/lib/include/check_completed_tasks.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/toloka_manager_cron_jobs/lib/include/create_new_tasks.h>
#include <maps/wikimap/mapspro/services/mrc/long_tasks/toloka_manager_cron_jobs/lib/include/delete_free_tasks.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/libs/common/include/environment.h>
#include <maps/libs/common/include/exception.h>
#include <yandex/maps/mds/mds.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/pg_locks.h>
#include <yandex/maps/mrc/toloka_client/client.h>
#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>

#include <chrono>
#include <memory>

namespace mrc = maps::mrc;

namespace {

const std::string MDS_PATH_PREFIX = "toloka-mgr/task";

maps::mds::Mds makePublicMds(const maps::mrc::common::Config& config)
{
    maps::mds::Configuration mdsConfig = config.makePublicMdsConfiguration();
    mdsConfig.setPathPrefix(MDS_PATH_PREFIX);
    return maps::mds::Mds(mdsConfig);
}

template <typename Functor>
void run(const std::string& logPrefix, Functor&& func) try {
    INFO() << logPrefix << "...";
    func();
    INFO() << logPrefix << " done";
}
catch (const maps::Exception& e) {
    ERROR() << logPrefix << " failed: " << e;
}
catch (const std::exception& e) {
    ERROR() << logPrefix << " failed: " << FormatCurrentException();
}


} // anonymous namespace

int main(int argc, char* argv[]) try {
    maps::cmdline::Parser parser;
    auto configPath = parser.string("config")
        .help("path to configuration");

    auto platformOpt = parser.string("platform")
        .required()
        .help("Platform to operate with ('toloka' or 'yang')");

    auto secretVersion = parser.string("secret-version")
        .help("version for secrets from yav.yandex-team.ru");

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

    auto platform =
        maps::enum_io::fromString<maps::mrc::db::toloka::Platform>(platformOpt);

    auto cfg = [&]() {
        if (secretVersion.defined()) {
            return maps::mrc::common::templateConfigFromCmdPath(secretVersion, configPath);
        } else {
            return maps::mrc::common::templateConfigFromCmdPath(configPath);
        }
    }();

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

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

    auto poolHolder = cfg.makePoolHolder(maps::mrc::common::LONG_READ_DB_ID,
                                         maps::mrc::common::LONG_READ_POOL_ID);
    auto& pool = poolHolder.pool();

    auto lock = platform == maps::mrc::db::toloka::Platform::Toloka
        ? maps::mrc::common::LockId::TolokaManager
        : maps::mrc::common::LockId::YangManager;

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

    const auto& crowdPlatformConfig = cfg.crowdPlatformConfig(platform);

    mrc::toloka::io::TolokaClient tolokaClient(
        crowdPlatformConfig.host(), crowdPlatformConfig.authHeader());
    tolokaClient.setTimeout(std::chrono::seconds(10))
                .setMaxRequestAttempts(6)
                .setRetryInitialTimeout(std::chrono::seconds(1))
                .setRetryTimeoutBackoff(2);
    auto mdsClient = makePublicMds(cfg);

    run("Delete free tasks",
        [&] { mrc::toloka::deleteFreeTasks(platform, pool, mdsClient); });

    run("Check completed tasks",
        [&] { mrc::toloka::checkCompletedTasks(platform, pool, tolokaClient); });

    run("Create new tasks",
        [&] { mrc::toloka::createNewTasks(platform, cfg, pool, tolokaClient); });

    run("Cancel tasks",
        [&] { mrc::toloka::cancelTasks(platform, pool, tolokaClient); });

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