#include "eye/frame.h"
#include "eye/object_gateway.h"
#include "eye/recognition.h"
#include "object_manager_fixture.h"

#include <maps/wikimap/mapspro/services/mrc/eye/lib/location/include/location.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/object_manager/impl/location.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/object_manager/impl/batch.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/object_manager/impl/metadata.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/object_manager/include/object_manager.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/unit_test/include/frame.h>

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/frame_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/recognition_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/algorithm/collection.h>

#include <maps/libs/sql_chemistry/include/system_information.h>

#include <library/cpp/testing/gtest/gtest.h>

namespace maps::mrc::eye::tests {

namespace {

struct ObjectRelationFixture: public ObjectManagerFixture {

    ObjectRelationFixture() {
        devices = insertx<db::eye::DeviceGateway>(
            db::eye::Devices {
                {db::eye::MrcDeviceAttrs{""}}
            }
        );

        frames = insertx<db::eye::FrameGateway>(
            db::eye::Frames {
                {devices[0].id(), identical, makeUrlContext(1, "1s"), {384, 216}, time()}, // 0
            }
        );

        frameLocations = insertx<db::eye::FrameLocationGateway> (
            db::eye::FrameLocations {
                {frames[0].id(), {0, 0}, toRotation(geolib3::Heading(90), identical)},
            }
        );
        for (const auto& frame : frames) {
            framePrivacies.emplace_back(frame.id(), db::FeaturePrivacy::Public);
        }
        framePrivacies = insertx<db::eye::FramePrivacyGateway>(std::move(framePrivacies));

        groups = insertx<db::eye::DetectionGroupGateway>(
            db::eye::DetectionGroups {
                {frames[0].id(), db::eye::DetectionType::Sign},
            }
        );

        detections = insertx<db::eye::DetectionGateway>(
            db::eye::Detections {
                // Sign 0
                {groups[0].id(), db::eye::DetectedSign{{315, 80, 345, 110}, traffic_signs::TrafficSign::ProhibitoryNoParkingOrStopping, 1.0, false, 0.0}}, // 0
                // Table 0
                {groups[0].id(), db::eye::DetectedSign{{310, 120, 355, 150 }, traffic_signs::TrafficSign::InformationStartZone, 1.0, false, 0.0}}, // 10
            }
        );
    }
};

} // namespace


TEST_F(ObjectRelationFixture, no_object_relation_without_detection_relation)
{
    auto workerConfig = makeWorkerConfig({db::eye::DetectionType::Sign});
    ObjectManager objectManager(workerConfig);
    objectManager.processBatchInLoopMode(10);

    auto txn = newTxn();
    auto objectRelationsCount = db::eye::ObjectRelationGateway(*txn).count();
    EXPECT_EQ(objectRelationsCount, 0u);
}

TEST_F(ObjectRelationFixture, create_object_relation)
{
    auto workerConfig = makeWorkerConfig({db::eye::DetectionType::Sign});
    ObjectManager objectManager(workerConfig);
    objectManager.processBatchInLoopMode(10);

    {
        auto txn = newTxn();
        db::eye::DetectionRelationGateway(*txn).insertx(
            db::eye::DetectionRelations {
                { detections[0].id(), detections[1].id() }
            }
        );
        db::eye::DetectionGroupGateway(*txn).updatex(groups[0]);
        txn->commit();
    }

    objectManager.processBatchInLoopMode(10);

    auto txn = newTxn();
    auto objectRelations = db::eye::ObjectRelationGateway(*txn).load();

    auto idToObject = maps::mrc::common::byId(db::eye::ObjectGateway(*txn).load());

    ASSERT_EQ(objectRelations.size(), 1u);
    EXPECT_EQ(idToObject.at(objectRelations[0].masterObjectId()).primaryDetectionId(),
        detections[0].id());
    EXPECT_EQ(idToObject.at(objectRelations[0].slaveObjectId()).primaryDetectionId(),
        detections[1].id());
}


TEST_F(ObjectRelationFixture, delete_object_relation_on_detection_relation_deletion)
{
    db::eye::DetectionRelations detectionRelations;
    {
        auto txn = newTxn();
        detectionRelations = insertx<db::eye::DetectionRelationGateway>(
            db::eye::DetectionRelations {
                { detections[0].id(), detections[1].id() }
            }
        );
        db::eye::DetectionGroupGateway(*txn).updatex(groups[0]);
        txn->commit();
    }

    auto workerConfig = makeWorkerConfig({db::eye::DetectionType::Sign});
    ObjectManager objectManager(workerConfig);
    objectManager.processBatchInLoopMode(10);

    {
        detectionRelations[0].setDeleted(true);
        auto txn = newTxn();
        db::eye::DetectionRelationGateway(*txn).updatex(detectionRelations);
        db::eye::DetectionGroupGateway(*txn).updatex(groups[0]);
        txn->commit();
    }

    objectManager.processBatchInLoopMode(10);

    auto txn = newTxn();
    auto objectRelations = db::eye::ObjectRelationGateway(*txn).load();

    ASSERT_EQ(objectRelations.size(), 1u);
    EXPECT_EQ(objectRelations[0].deleted(), true);
}


} // namespace maps::mrc::eye::tests

