#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/threadpool_wrapper.h>

#include <algorithm>
#include <functional>
#include <locale>
#include <span>
#include <sstream>
#include <vector>

namespace maps::mrc::blur_mds {

std::string concat(const auto&... args)
{
    auto os = std::ostringstream{};
    os.imbue(std::locale(""));
    ((os << args), ...);
    return os.str();
}

template <size_t ThreadsNumber, size_t BatchSize, class Range, class Function>
void parallelForEachBatch(Range&& range, Function fn)
{
    auto threadPool = common::ThreadpoolWrapper{ThreadsNumber};
    auto first = std::begin(range);
    for (size_t pos = 0, count = std::size(range); pos < count;) {
        auto prev = pos;
        pos += std::min<size_t>(count - pos, BatchSize);
        auto batch = std::span(first + prev, first + pos);
        threadPool->add([&fn, batch] { std::invoke(fn, batch); });
    }
    threadPool->drain();
    threadPool.checkExceptions();
}

template <class Range, class Function>
auto invokeForEach(Range&& range, Function fn)
{
    using From = decltype(*std::begin(range));
    using To = std::invoke_result_t<Function, From>;
    auto result = std::vector<std::remove_cvref_t<To>>{};
    for (const auto& item : range) {
        result.push_back(std::invoke(fn, item));
    }
    return result;
}

inline size_t ceil(size_t num, size_t denom)
{
    ASSERT(denom);
    return num - num % denom;
}

}  // namespace maps::mrc::blur_mds
