#pragma once

#include "context.h"
#include "features_index.h"
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/geometry.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/visibility.h>

#include <mapreduce/yt/interface/client.h>
#include <maps/libs/chrono/include/days.h>
#include <maps/libs/edge_persistent_index/include/persistent_index.h>
#include <maps/libs/introspection/include/comparison.h>
#include <maps/libs/introspection/include/stream_output.h>
#include <maps/libs/road_graph/include/graph.h>
#include <maps/libs/sql_chemistry/include/types.h>
#include <maps/libs/succinct_rtree/include/rtree.h>

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

#include <pqxx/transaction_base>

#include <library/cpp/geobase/lookup.hpp>

#include <optional>
#include <ostream>
#include <string>
#include <unordered_set>
#include <vector>

namespace maps::mrc::graph_coverage_export {

using maps::introspection::operator<;
using maps::introspection::operator>;
using maps::introspection::operator==;
using maps::introspection::operator<<;

struct CoverageData {
    chrono::Days from;
    chrono::Days to;
    chrono::Days actualizationDate;
    common::geometry::SubPolyline subpolyline;

    template<typename T>
    static auto introspect(T& o) {
        return std::tie(o.from, o.to, o.actualizationDate, o.subpolyline);
    }
};

using CoverageDataVec = std::vector<CoverageData>;

db::Features
searchFeaturesForEdge(const geolib3::Polyline2& edgeGeometry,
                      const FeaturesIndex& featuresIndex);

std::optional<common::geometry::SubSegment>
evalSegmentCoverage(
    const geolib3::Segment2& geoSegment,
    geolib3::Direction2 direction,
    const geolib3::Polygon2& fieldOfView);


template<typename OutputIt>
void evalPolylineCoverage(
    const geolib3::Polyline2& polyline,
    const db::Feature& feature,
    OutputIt outputIt)
{
    auto view = db::fieldOfView(db::getRay(feature));
    geolib3::Direction2 direction(feature.heading());
    for (size_t i = 0; i < polyline.segmentsNumber(); ++i) {
        auto maybeCoverage = evalSegmentCoverage(polyline.segmentAt(i), direction, view);
        if (maybeCoverage) {
            *outputIt++ = common::geometry::SubPolyline{
                {i, maybeCoverage->begin()},
                {i, maybeCoverage->end()}};
        }
    }
}

CoverageDataVec evalPolylineCoverage(const geolib3::Polyline2& polyline,
                                     db::Features::const_iterator begin,
                                     db::Features::const_iterator end);


void convertToCoverage(NYT::IClientBase& ytClient,
                       const TString& fromTable,
                       const TString& toTable);

double calcCoverageLength(const geolib3::Polyline2& mercatorGeom,
                          const common::geometry::SubPolyline& subpolyline);

chrono::Days maxDate();

} // namespace maps::mrc::graph_coverage_export

