#pragma once
#include "common.h"
#include "metadata.h"
#include "nexar_client.h"

#include <maps/wikimap/mapspro/services/mrc/libs/common/include/geometry.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/import_config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/nexar_import_tile_update_info.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/read.h>
#include <maps/wikimap/mapspro/services/mrc/libs/privacy/include/geo_id_provider.h>

#include <maps/libs/chrono/include/time_point.h>
#include <maps/libs/geolib/include/polyline.h>
#include <maps/libs/mds-client/include/yandex/maps/mds/mds.h>
#include <maps/libs/pgpool/include/pgpool3.h>
#include <maps/libs/road_graph/include/graph.h>
#include <maps/libs/succinct_rtree/include/rtree.h>

#include <pqxx/transaction_base>

namespace maps::mrc::import_nexar {

db::ImportConfigs loadImportConfigs(pqxx::transaction_base& txn);

std::vector<TileImportParams> evalTileImportParams(
    const privacy::GeoIdProvider& geoIdProvider,
    const db::ImportConfig& config,
    int32_t tilingZoom);

bool shouldUpdateTile(
    const db::ImportConfig& config,
    const db::NexarImportTileUpdateInfo& tileUpdateInfo,
    chrono::TimePoint checkTime);

std::vector<TileImportParams> evalTileImportParamsToCheck(
    const Metadata& metadata,
    const db::ImportConfig& config,
    std::vector<TileImportParams> params,
    chrono::TimePoint checkTime);

struct EdgeWithGeometry {
    road_graph::EdgeId edgeId;
    geolib3::Polyline2 geometry;
};

std::vector<EdgeWithGeometry>
evalGeodeticPolylinesToImport(const road_graph::Graph& graph,
                      const succinct_rtree::Rtree& rtree,
                      const TileImportParams& param);


struct ImageWithCoverage {
    NexarImageMeta imageMeta;
    road_graph::EdgeId edgeId;
    common::geometry::SubPolyline subpolyline;

    template<typename T>
    static auto introspect(T& t)
    {
        return std::tie(t.imageMeta, t.edgeId, t.subpolyline);
    }
};

using SubpolylineWithImageRef =
        common::geometry::SubPolylineWithValue<NexarImageMetaCRef>;

/// Calculates part of the @param polyline that is visible from
/// the @param image
common::geometry::SubPolylines
evalCoverage(const geolib3::Polyline2& geoPolyline, NexarImageMetaCRef image);

std::vector<SubpolylineWithImageRef>
evalUniqueActualCoverage(const geolib3::Polyline2& polyline,
                         NexarImageMetaCRefs images);

/// Selects the most actual and minimal set of frames covering
/// edges of @graph in given @param param tile
std::vector<ImageWithCoverage> selectBestCoveringFrames(
    const road_graph::Graph& graph,
    const succinct_rtree::Rtree& rtree,
    const TileImportParams& param,
    const INexarClient& nexarClient);

/// @return whether image is worth importing
bool improvesEdgeCoverage(
    const ImageWithCoverage& imageWithCoverage, const fb::TEdge& edge);

std::vector<NexarImageMeta>
selectImagesThatImproveCurrentCoverage(
    const std::vector<ImageWithCoverage>& imagesWithCoverage,
    const fb::GraphReader& mrcGraphReader);

std::optional<chrono::TimePoint>
evalMedianImageAge(const std::vector<ImageWithCoverage>& imagesWithCoverage);

db::Feature convertToFeature(const NexarImageMeta& meta, const std::string& image);

db::Feature importImage(
    pgpool3::Pool& pool,
    const INexarClient& nexarClient,
    mds::Mds& mdsClient,
    const NexarImageMeta& imageMeta);

/// Imports from Nexar photos that improves current coverage in @param tile
db::NexarImportTileUpdateInfo importImagesForTile(
    pgpool3::Pool& pool,
    mds::Mds& mdsClient,
    const fb::GraphReader& mrcGraphReader,
    const road_graph::Graph& graph,
    const succinct_rtree::Rtree& rtree,
    const INexarClient& nexarClient,
    const TileImportParams& tile
);

} // namespace maps::mrc::import_nexar
