#include "passage_loader.h"

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/libs/sql_chemistry/include/batch_load.h>
#include <maps/libs/sql_chemistry/include/order.h>
#include <maps/libs/common/include/make_batches.h>
#include <maps/libs/log8/include/log8.h>
#include <boost/range/algorithm_ext/erase.hpp>

#include <algorithm>


namespace maps::mrc::sideview_classifier {

namespace {

template <class FeatureIt>
PassageKeyToFeaturesMap splitIntoPassages(FeatureIt first, FeatureIt last)
{
    constexpr auto LIMIT = 500;
    constexpr auto TIME_GAP = std::chrono::seconds{15};
    PassageKeyToFeaturesMap passagesByKey;

    while (first != last) {
        auto limit = first;
        std::advance(limit, std::min<int>(std::distance(first, last), LIMIT));
        auto next = std::adjacent_find(
            first, limit, [&](const auto& lhs, const auto& rhs) {
                return lhs.sourceId() != rhs.sourceId()
                       || std::chrono::abs(lhs.timestamp() - rhs.timestamp())
                              > TIME_GAP;
            });

        if (next != limit)
            next = std::next(next);

        PassageKey key{first->sourceId(), first->id()};
        for (auto itr = first; itr != next; ++itr) {
            passagesByKey[key].push_back(std::move(*itr));
        }

        first = next;
    }
    return passagesByKey;
}

}

PassageKeyToFeaturesMap loadPassages(
    maps::pgpool3::Pool& pool,
    db::TIds featureIds)
{
    auto txn = pool.slaveTransaction();
    db::FeatureGateway gtw(*txn);

    INFO() << "Process " << featureIds.size() << " feature IDs"
            << ". min featureID = " << featureIds.front()
            << ", max featureID = " << featureIds.back();

    db::Features features;
    features.reserve(featureIds.size());
    for (const auto& batch : maps::common::makeBatches(featureIds, 1000)) {
        auto part = gtw.loadByIds({batch.begin(), batch.end()});
        std::move(part.begin(), part.end(), std::back_inserter(features));
    }

    std::sort(features.begin(), features.end(),
              [](const auto& lhs, const auto& rhs) {
                  return std::make_tuple(lhs.sourceId(), lhs.timestamp())
                         < std::make_tuple(rhs.sourceId(), rhs.timestamp());
              });

    return splitIntoPassages(features.begin(), features.end());
}

} // namespace maps::mrc::sideview_classifier
