#pragma once

#include "common.h"

#include <maps/libs/chrono/include/time_point.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/track_point.h>
#include <maps/libs/common/include/math.h>
#include <maps/libs/deprecated/localeutils/include/locale.h>
#include <yandex/maps/mds/mds.h>
#include <maps/wikimap/mapspro/services/mrc/libs/toloka_manager/include/image_quality_classification.h>

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/optional.hpp>
#include <boost/range/adaptor/sliced.hpp>

#include <initializer_list>
#include <type_traits>

namespace maps {
namespace mrc {
namespace img_qa {

TolokaStatistics
makeTolokaStatistics(db::TId assignmentId,
                     size_t total,
                     const PhotoIdToTolokaStatusMap& tolokaPhotoToStatusMap);

db::ugc::TolokaStatus status(const TolokaStatistics& stat);

std::string toString(const TolokaStatistics& stat);

std::string toString(const CoverageStatistics& stat);

void sortByQualityDesc(db::Features& photos);

db::FeatureQaTasks
zip(FeatureBatch photos, const toloka::ImageQualityC12nTasks& tasks);

chrono::TimePoint fromBoost(const boost::posix_time::ptime&);

boost::posix_time::ptime toBoost(const chrono::TimePoint&);

void sortUniqueByTime(db::TrackPoints&);

TrackSegments makePath(chrono::TimePoint startTime,
                       chrono::TimePoint endTime,
                       const Segments& segments);

TrackSegments makePath(const db::TrackPoints&);

Segments getSegments(const geolib3::Polyline2&);

Segments getSegments(const std::initializer_list<geolib3::Polyline2>&);

Segments getSegments(const db::ugc::Task&);

Segments getSegments(const TrackSegments&);

template <typename T>
std::vector<T>& operator+=(std::vector<T>& lhs, const std::vector<T>& rhs)
{
    lhs.insert(lhs.end(), rhs.begin(), rhs.end());
    return lhs;
}

// returns true if track distance or coverage were changed
bool areRideResultsChanged(const db::ugc::AssignmentReview& review,
                           const img_qa::CoverageStatistics& coverageStats);

bool isPhotoGood(const db::Feature&, const PhotoIdToTolokaStatusMap&);

/**
 * maps::common::make_batches() supports const iterators only
 * @see
 * https://a.yandex-team.ru/arc/trunk/arcadia/maps/libs/common/include/yandex/maps/common/make_batches.h
 */
template <typename Container, typename Consumer>
void slice(Container& container, const Consumer& consumer)
{
    constexpr size_t BATCH_SIZE = 1000;
    size_t pos = 0;
    while (pos < container.size()) {
        auto previous = pos;
        pos += std::min(container.size() - pos, BATCH_SIZE);
        auto range = boost::adaptors::slice(container, previous, pos);
        consumer(range.begin(), range.end());
    }
}

geolib3::PolylinesVector toPolylines(const Segments& segments);

geolib3::PolylinesVector toEquidistantPolylines(const Segments& segments);

template <typename Geometries>
double geoLength(const Geometries& geoms)
{
    return maps::common::sumKahan(
        geoms,
        [](const typename Geometries::value_type& geom) {
            return geolib3::geoLength(geom);
        });
}

std::string getName(const db::ugc::Task& task, const Locale& locale);

inline chrono::TimePoint currentTimePoint()
{
    return chrono::TimePoint::clock::now();
}

inline chrono::TimePoint timePointOfStart()
{
    static const auto result = currentTimePoint();
    return result;
}

inline time_t timeOfStart()
{
    return chrono::convertToUnixTime(timePointOfStart());
}

void fillReview(const CoverageStatistics& coverageStat,
                db::ugc::AssignmentReview& review);

std::string mdsKeyToJson(const mds::Key& key);

mds::Key jsonToMdsKey(const std::string& json);

} // img_qa
} // mrc
} // maps
