#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/utility.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>

namespace maps::mrc::ride_inspector {

const auto MIN_TIME_GAP_BETWEEN_RIDES = std::chrono::seconds{300};

inline const std::string& getUserId(const db::Feature& feature)
{
    ASSERT(feature.userId().has_value());
    return feature.userId().value();
}

inline const std::string& getSourceId(const db::Feature& feature)
{
    ASSERT(feature.sourceId() != db::feature::NO_SOURCE_ID);
    return feature.sourceId();
}

inline const std::optional<std::string>& getClientRideId(
    const db::Feature& feature)
{
    return feature.clientRideId();
}

inline chrono::TimePoint getTimestamp(const db::Feature& feature)
{
    return feature.timestamp();
}

template <class PhotoIt>
void sortByRide(PhotoIt first, PhotoIt last)
{
    auto slicePhoto = common::makeTupleFn(
        getUserId, getSourceId, getClientRideId, getTimestamp);
    std::sort(first, last, common::lessFn(slicePhoto));
}

template <class PhotoIt, class Function>
void forEachRide(PhotoIt first, PhotoIt last, Function f)
{
    auto slicePhoto =
        common::makeTupleFn(getUserId, getSourceId, getClientRideId);
    auto equalRide = [&](auto& lhs, auto& rhs) {
        return slicePhoto(lhs) == slicePhoto(rhs) &&
               (lhs.clientRideId().has_value() ||
                abs(getTimestamp(lhs) - getTimestamp(rhs)) <=
                    MIN_TIME_GAP_BETWEEN_RIDES);
    };
    common::forEachEqualRange(first, last, equalRide, f);
}

template <class Function, class Entities>
auto invokeForEach(Function fn, const Entities& entities)
{
    auto result = std::vector<
        std::invoke_result_t<Function, decltype(*std::begin(entities))>>{};
    for (const auto& entity : entities) {
        result.push_back(std::invoke(fn, entity));
    }
    return result;
}

}  // namespace maps::mrc::ride_inspector
