#include "helpers.h"

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

#include <atomic>
#include <chrono>
#include <future>
#include <memory>
#include <utility>

namespace maps::wiki::json2ymapsdf::isocode {

namespace {

enum class SafeRunnerMode { Quiet, Normal };

void
safeRunner(
    const std::function<void()>& f,
    const std::string& name,
    const std::shared_ptr<std::atomic<bool>>& fail,
    SafeRunnerMode mode)
{
    if (*fail) {
        return;
    }

    if (mode == SafeRunnerMode::Normal) {
        INFO() << name << " started";
    }
    try {
        f();
        if (mode == SafeRunnerMode::Normal) {
            INFO() << name << " finished";
        }
    } catch (const maps::RuntimeError& rtErr) {
        ERROR() << name << " failed: " << rtErr;
        *fail = true;
    } catch (std::exception& ex) {
        ERROR() << name << " failed: " << ex.what();
        *fail = true;
    } catch (...) {
        ERROR() << name << " failed: Unknown exception";
        *fail = true;
    }
}

std::string
signatureToName(const std::string& funcSignature)
{
    const auto roundBracket = funcSignature.rfind('(');
    const auto doubleColon = funcSignature.rfind("::", roundBracket);
    size_t start = doubleColon == std::string::npos
        ? 0
        : doubleColon + 2;
    size_t length = roundBracket == std::string::npos
        ? std::string::npos
        : roundBracket - start;

    return funcSignature.substr(start, length);
}

} // namespace

pqxx::result
exec(
    pqxx::transaction_base& work,
    const std::string& query,
    const std::string& funcSignature)
{
    const auto funcName = signatureToName(funcSignature);
    INFO() << "start " << funcName;

    const auto start = std::chrono::system_clock::now();

    DEBUG() << funcName << ":" << query;

    const auto res = work.exec(query);

    const auto end = std::chrono::system_clock::now();

    const auto elapsedSeconds = std::chrono::duration_cast<std::chrono::seconds>(end - start);

    INFO() << funcName << ": "
        << elapsedSeconds.count() << " sec, "
        << res.affected_rows() << " rows.";

    return res;
}

void
safeRunner(
    const std::function<void()>& f,
    const std::string& name,
    const std::shared_ptr<std::atomic<bool>>& fail)
{
    safeRunner(f, name, fail, SafeRunnerMode::Normal);
}

void
safeRunnerQuiet(
    const std::function<void()>& f,
    const std::string& name,
    const std::shared_ptr<std::atomic<bool>>& fail)
{
    safeRunner(f, name, fail, SafeRunnerMode::Quiet);
}

} // namespace maps::wiki::json2ymapsdf::isocode
