#pragma once

#include <maps/libs/edge_persistent_index/include/persistent_index.h>
#include <maps/libs/road_graph/include/graph.h>
#include <maps/libs/succinct_rtree/include/rtree.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/sequenced_lifetime_guard.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/coverage_rtree_reader.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/photo_to_edge_pairs_reader.h>
#include <maps/wikimap/mapspro/services/mrc/libs/fb/include/read.h>

#include <memory>
#include <string>

namespace maps::mrc::browser {


class IGraph {
public:

    virtual ~IGraph() = default;

    virtual const road_graph::Graph& graph() const = 0;

    virtual const succinct_rtree::Rtree& rtree() const = 0;

    virtual const road_graph::PersistentIndex& persistentIndex() const = 0;

    virtual std::vector<fb::CoveredSubPolyline>
        getCoveredSubpolylinesForEdgeSegments(
            road_graph::EdgeId,
            const std::vector<road_graph::SegmentIndex>& sortedSegments,
            db::CameraDeviation,
            db::FeaturePrivacy) const = 0;

    virtual std::vector<road_graph::EdgeId> getCoveredEdgeIdsByBbox(
        const geolib3::BoundingBox& geoBbox,
        db::TFc maxFc) const = 0;

    virtual std::vector<road_graph::EdgeId> getNearestCoveredEdges(
        const geolib3::Point2& geoPos,
        size_t limit,
        db::TFc maxFc) const = 0;

    virtual db::TIds getFeatureIdsByBbox(const geolib3::BoundingBox& geoBbox) const = 0;

    virtual db::TIds getFeatureIdsByEdgeId(road_graph::EdgeId) const = 0;

    virtual std::optional<fb::TEdge> getCoveredEdgeById(
        road_graph::EdgeId) const = 0;

    virtual std::size_t getEdgeSegmentsNum(road_graph::EdgeId) const = 0;

    virtual std::optional<chrono::TimePoint> getGeneratedAt() const = 0;
};


using IGraphPtr = std::shared_ptr<IGraph>;

class Graph : public IGraph {
public:
    Graph(const std::string& graphFolder, EMappingMode);

    const road_graph::Graph& graph() const override { return graph_; }

    const succinct_rtree::Rtree& rtree() const override { return rtree_; }

    const road_graph::PersistentIndex& persistentIndex() const override
    {
        return persistentIndex_;
    }

    std::vector<fb::CoveredSubPolyline> getCoveredSubpolylinesForEdgeSegments(
        road_graph::EdgeId,
        const std::vector<road_graph::SegmentIndex>& sortedSegments,
        db::CameraDeviation,
        db::FeaturePrivacy) const override;

    std::vector<road_graph::EdgeId> getCoveredEdgeIdsByBbox(
        const geolib3::BoundingBox& geoBbox,
        db::TFc maxFc) const override;

    std::vector<road_graph::EdgeId> getNearestCoveredEdges(
        const geolib3::Point2& geoPos,
        size_t limit,
        db::TFc maxFc) const override;

    db::TIds getFeatureIdsByBbox(const geolib3::BoundingBox& geoBbox) const override;

    db::TIds getFeatureIdsByEdgeId(road_graph::EdgeId) const override;

    std::optional<fb::TEdge> getCoveredEdgeById(
        road_graph::EdgeId) const override;

    std::size_t getEdgeSegmentsNum(road_graph::EdgeId) const override;

    std::optional<chrono::TimePoint> getGeneratedAt() const override { return generatedAt_; }

private:
    road_graph::Graph graph_;
    succinct_rtree::Rtree rtree_;
    road_graph::PersistentIndex persistentIndex_;
    fb::GraphReader coverage_;
    fb::CoverageRtreeReader coverageRtreeReader_;
    std::optional<chrono::TimePoint> generatedAt_;
};

struct RoadGraph : common::SequencedLifetimeGuard<RoadGraph>, Graph {
    using Graph::Graph;
};

struct PedestrianGraph : common::SequencedLifetimeGuard<PedestrianGraph>, Graph {
    using Graph::Graph;
};


class IPhotoToEdge {
public:
    virtual ~IPhotoToEdge() = default;

    virtual std::vector<fb::PhotoToEdgePair> lookupByFeatureId(
        db::TId featureId) const = 0;

    virtual std::optional<chrono::TimePoint> getGeneratedAt() const = 0;
};

using IPhotoToEdgePtr = std::shared_ptr<IPhotoToEdge>;

class PhotoToEdge : public IPhotoToEdge {
public:
    PhotoToEdge(const std::string& photoToEdgeFolder, EMappingMode);

    std::vector<fb::PhotoToEdgePair> lookupByFeatureId(
        db::TId featureId) const override;

    std::optional<chrono::TimePoint> getGeneratedAt() const override
    {
        return generatedAt_;
    }

private:
    fb::PhotoToEdgePairsReader reader_;
    chrono::TimePoint generatedAt_;
};

struct RoadPhotoToEdge
    : common::SequencedLifetimeGuard<RoadPhotoToEdge>
    , PhotoToEdge
{
    using PhotoToEdge::PhotoToEdge;
};

struct PedestrianPhotoToEdge
    : common::SequencedLifetimeGuard<PedestrianPhotoToEdge>
    , PhotoToEdge
{
    using PhotoToEdge::PhotoToEdge;
};

}  // namespace maps::mrc::browser
