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

#include <maps/wikimap/mapspro/services/tasks_feedback/src/matview_refresher_worker/lib/worker.h>

#include <yandex/maps/pgpool3utils/pg_advisory_mutex.h>
#include <yandex/maps/wiki/common/pg_advisory_lock_ids.h>
#include <yandex/maps/wiki/social/feedback/mv_source_type.h>
#include <yandex/maps/wiki/social/feedback/task_aoi.h>

#include <functional>
#include <future>

namespace maps::wiki::matview_refresher {

namespace sf = social::feedback;


void Worker::refreshSourceTypeStatRoutine()
{
    auto hndlSocialWrite = socialPool_.masterWriteableTransaction();

    sf::refreshSourceTypeMv(hndlSocialWrite.get());

    hndlSocialWrite->commit();

    INFO() << "Source type matview is refreshed.";
}

void Worker::refreshAoiOldestTaskViewRoutine()
{
    auto hndlSocialWrite = socialPool_.masterWriteableTransaction();

    sf::refreshAoiOldestTaskMv(hndlSocialWrite.get());

    hndlSocialWrite->commit();

    INFO() << "FeedbackAoiOldestTask matview is refreshed.";
}

void Worker::refreshAoiTaskStatViewRoutine()
{
    auto hndlSocialWrite = socialPool_.masterWriteableTransaction();

    sf::refreshAoiTaskStatMv(hndlSocialWrite.get());

    hndlSocialWrite->commit();

    INFO() << "FeedbackAoiTaskStat matview is refreshed.";
}

Worker::Worker(
        pgpool3::Pool& socialPool,
        tasks::StatusWriter& statusWriter)
    : socialPool_(socialPool)
    , statusWriter_(statusWriter)
{}

void Worker::doTask()
{
    statusWriter_.reset();
    INFO() << "Task started.";

    //locking database
    pgp3utils::PgAdvisoryXactMutex dbLocker(
        socialPool_,
        static_cast<int64_t>(common::AdvisoryLockIds::MATVIEW_REFRESHER));
    if (!dbLocker.try_lock()) {
        INFO() << "Database is already locked. Task interrupted.";
        return;
    }

    std::map<std::string, std::future<void>> namedFutures;
    namedFutures.emplace("refresh source type matview",
                         std::async(std::launch::async, [&](){
                                 refreshSourceTypeStatRoutine(); } ));

    namedFutures.emplace("refresh aoi oldest task matview",
                         std::async(std::launch::async, [&](){
                                 refreshAoiOldestTaskViewRoutine(); } ));
    namedFutures.emplace("refresh aoi task stat matview",
                         std::async(std::launch::async, [&](){
                                 refreshAoiTaskStatViewRoutine(); } ));

    auto constructRoutineError =
        [](const std::string& routineName, const std::string& error) {
            return routineName + " failed. " + error;
        };

    std::vector<std::string> errs;

    auto noExceptCall = [&](const std::string& name, const auto& routine) {
        try {
            INFO() << "Routine " << name << " started.";
            routine();
            INFO() << "Routine " << name << " finished.";
        } catch (const Exception& ex) {
            auto exStr = (std::stringstream() << ex).str();
            errs.emplace_back(constructRoutineError(name, exStr));
        } catch (const std::exception& ex) {
            errs.emplace_back(constructRoutineError(name, ex.what()));
        } catch (...) {
            errs.emplace_back(constructRoutineError(name,"unknown error"));
        }
    };

    for (auto& namedFuture : namedFutures) {
        noExceptCall(namedFuture.first, [&](){ namedFuture.second.get(); });
    }

    for (const auto& err : errs) {
        statusWriter_.err(err);
        ERROR() << err;
    }

    statusWriter_.flush();
}

} // namespace maps::wiki::matview_refresher
