#pragma once

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

#include <functional>
#include <unordered_map>
#include <utility>
#include <vector>

namespace maps::mrc::common {

template <typename Collection,
          typename ObjectType = std::conditional_t<std::is_const_v<Collection>,
                                                   std::add_const_t<typename Collection::value_type>,
                                                   typename Collection::value_type>,
          typename IdType = typename std::decay_t<decltype(std::declval<ObjectType>().id())>>
std::unordered_map<IdType, std::decay_t<ObjectType>> byId(Collection&& objects)
{
    std::unordered_map<IdType, std::decay_t<ObjectType>> result;
    result.reserve(objects.size());

    for (auto&& object : objects) {
        auto id = object.id();
        result.emplace(id, std::forward<ObjectType>(object));
    }
    return result;
}

template<typename Collection,
    typename Function,
    typename KeyType = std::remove_cvref_t<
        std::invoke_result_t<Function, typename Collection::value_type>>,
    typename ObjectType =
        std::conditional_t<std::is_const_v<Collection>,
            std::add_const_t<typename Collection::value_type>,
            typename Collection::value_type>,
    typename ReferenceType = std::reference_wrapper<ObjectType>>
std::unordered_map<KeyType, ReferenceType>
makeRefMap(Collection& objects, Function func)
{
    std::unordered_map<KeyType, ReferenceType> result;
    result.reserve(objects.size());

    for (auto& object : objects) {
        auto key = std::invoke(func, object);
        auto [it, inserted] = result.emplace(key, std::ref(object));
        REQUIRE(inserted, "Duplicate key found");
    }
    return result;
}

template <typename T>
void sortUnique(std::vector<T>& ids)
{
    std::sort(ids.begin(), ids.end());
    auto it = std::unique(ids.begin(), ids.end());
    ids.erase(it, ids.end());
}

} // namespace maps::mrc::common

