#pragma once

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/object.h>

#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/id.h>

#include <library/cpp/iterator/iterate_values.h>

#include <pqxx/pqxx>

#include <map>

namespace maps::mrc::eye {

class ObjectStore {

public:
    // Загружает все связи у которых primaryId совпадают
    // с primaryId объектов, а также подгружает для каждого
    // объекта его положение
    ObjectStore& extendByObjects(pqxx::transaction_base& txn, db::eye::Objects objects);

    // Находит для детекций из detectionIds их главные
    // детекции (детекция сама может быть главной) и загружает
    // объекты и их положения, а также связи соответсвующие
    // этим главным детекциям
    ObjectStore& extendByDetectionIds(pqxx::transaction_base& txn, const db::TIds& detectionIds);
    ObjectStore& extendByDetectionIds(pqxx::transaction_base& txn, const db::TIdSet& detectionIds);

    // Количество всех загруженных объектов (удаленных и не удаленных)
    size_t objectsCount() const;

    // Для итерирования по всему набору объектов в store, сюда будут включены
    // удаленные (deleted = true) и неудаленные (deleted = false) объекты.
    // Пример использования:
    //  for (const db::eye::Object& object : store.objects()) {
    //    ...
    //  }
    auto objects() const {
        return IterateValues(objectByPrimaryId_);
    }

    // Для итерирования по всему набору связей между детекциями в store,
    // сюда будут включены удаленные (deleted = true) и неудаленные (deleted = false)
    // связи между детекциями.
    // Пример использования:
    //  for (const db::eye::PrimaryDetectionRelation& relation : store.relations()) {
    //    ...
    //  }
    auto relations() const {
        return IterateValues(relationByIdPair_);
    }

    // Для итерирования по всему набору позиций объектов в store
    // Пример использования:
    //  for (const db::eye::ObjectLocation& location : store.objectLocations()) {
    //   ...
    //  }
    auto objectLocations() const {
        return IterateValues(locationByObjectId_);
    }

    // Возвращает множество всех актуальных загруженных
    // связей между детекциями в базе данных
    const db::IdTo<db::TIdSet>& relationMap() const;

    const db::eye::ObjectLocation& locationByObjectId(db::TId objectId) const;

    bool isPrimaryDetectionId(db::TId id) const;

private:
    // Собирает primaryId всех объектов, загружает из базы
    // все связи у которых primaryId содержатся в полученном множестве,
    // и добавляет их в relationByIdPair_ и relationMap_ (если связь актуальная)
    void addObjectsRelations(pqxx::transaction_base& txn, const db::eye::Objects& objects);
    // Загружает положения всех объектов и добавляет объекты в objectByPrimaryId_,
    // а их положения в locationByObjectId_;
    void addObjectsWithLocations(pqxx::transaction_base& txn, db::eye::Objects objects);

    // Все загруженные объекты (в том числе удаленные)
    db::IdTo<db::eye::Object> objectByPrimaryId_;
    // Положения всех загруженных объектов (в том числе удаленных)
    db::IdTo<db::eye::ObjectLocation> locationByObjectId_;

    // Все связи детекций загруженных объектов.
    // Т.е. если в objectByPrimaryId_ есть объект с primaryId,
    // то в relationByIdPair_ содержатся все связи из базы, в которых
    // primaryId совпадает с primaryId объекта (в том числе удаленные связи)
    // DetectionIdPair = (primaryId, detectionId)
    // db::eye::PrimaryDetectionRelation - связь из базы данных
    std::map<DetectionIdPair, db::eye::PrimaryDetectionRelation> relationByIdPair_;

    // Здесь содержатся только неудаленные связи
    // primaryId -> detectionIds. В detectionIds
    // не включена сама главная детекция primaryId.
    // Для объектов, у которых нет никаких других детекций
    // кроме primaryId, хранится пустая запись:
    // primaryId -> {}, т.е. detectionIds = {}.
    // Таким образом в этом структуре хранятся связи
    // с детекциями для всех неудаленных объектов, которые
    // есть в store. Итерируясь по этой структуре можно получить
    // primaryId всех неудаленных объектов.
    db::IdTo<db::TIdSet> relationMap_;
};

} // namespace maps::mrc::eye
