#include <mapreduce/yt/tests/yt_unittest_lib/yt_unittest_lib.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/sync_detection/impl/sync.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/sync_detection/tests/common.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/sync_detection/tests/fixture.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/recognition_gateway.h>

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

namespace {

const common::ImageOrientation identical{};

} // namespace

TEST_F(Fixture, new_recognitions)
{
    db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::Sign};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedSign one {
        {100, 100, 130, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed40,
        0.91,
        false,
        0.93
    };

    const db::eye::DetectedSign two {
        {100, 100, 130, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed60,
        0.90,
        false,
        0.91

    };

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectSign,
            db::eye::RecognitionSource::Model,
            1,
            db::eye::DetectedSigns{one, two}
        },
    };

    const auto [unchanged, updated, _] = makeUpdatedDetections(group, {}, recognitions);

    EXPECT_EQ(unchanged.size(), 0u);
    EXPECT_EQ(updated.size(), 2u);

    EXPECT_TRUE(checkDetectionFromGroup(group, updated));

    EXPECT_TRUE(hasDetectedSign(updated, one.type, one.box));
    EXPECT_TRUE(hasDetectedSign(updated, two.type, two.box));
}

TEST_F(Fixture, toloka_dominate)
{
     db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::Sign};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedSign modelSign {
        {100, 100, 130, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed40,
        0.9,
        true,
        0.8

    };

    const db::eye::DetectedSign firstTolokaSign {
        {120, 100, 140, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed40,
        0.9,
        false,
        0.99
    };

    const db::eye::DetectedSign secondTolokaSign {
        {120, 100, 140, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed45,
        0.9,
        true,
        0.99
    };

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectSign,
            db::eye::RecognitionSource::Model,
            1,
            db::eye::DetectedSigns{modelSign}
        },
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectSign,
            db::eye::RecognitionSource::Toloka,
            1,
            db::eye::DetectedSigns{firstTolokaSign}
        },
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectSign,
            db::eye::RecognitionSource::Toloka,
            2,
            db::eye::DetectedSigns{secondTolokaSign}
        }

    };

    const auto [unchanged, updated, _] = makeUpdatedDetections(group, {}, recognitions);

    EXPECT_EQ(unchanged.size(), 0u);
    EXPECT_EQ(updated.size(), 1u);

    EXPECT_TRUE(checkDetectionFromGroup(group, updated));

    EXPECT_TRUE(hasDetectedSign(updated, secondTolokaSign.type, secondTolokaSign.box));
}

TEST_F(Fixture, merge_for_sign)
{
    db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::Sign};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedSign one {
        {100, 100, 130, 130},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed40,
        0.9,
        false,
        0.95
    };

    const db::eye::DetectedSign two {
        {400, 400, 430, 430},
        traffic_signs::TrafficSign::ProhibitoryMaxSpeed60,
        0.9,
        false,
        0.95
    };

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectSign,
            db::eye::RecognitionSource::Model,
            1,
            db::eye::DetectedSigns{one, two}
        },
    };

    db::eye::Detections detections {
        {
            group.id(), db::eye::DetectedSign {
                {401, 399, 430, 430},
                traffic_signs::TrafficSign::ProhibitoryMaxSpeed60,
                0.9,
                false,
                0.95
            }
        },
        {
            group.id(), db::eye::DetectedSign {
                {200, 200, 230, 230},
                traffic_signs::TrafficSign::ProhibitoryMaxSpeed60,
                0.9,
                false,
                0.95
            }
        },
        {
            group.id(), db::eye::DetectedSign {
                {100, 100, 130, 130},
                traffic_signs::TrafficSign::ProhibitoryMaxSpeed40,
                0.9,
                true,
                0.95
            }
        }
    };

    {   // save  detections
        auto txn = newTxn();
        db::eye::DetectionGateway(*txn).insertx(detections);
        txn->commit();
    }

    const auto [unchanged, updated, _] = makeUpdatedDetections(group, detections, recognitions);

    EXPECT_EQ(unchanged.size(), 1u);
    EXPECT_EQ(updated.size(), 3u);

    EXPECT_TRUE(checkDetectionFromGroup(group, updated));

    EXPECT_TRUE(hasDetectedSign(updated, one.type, one.box));
    EXPECT_TRUE(isDeleted(updated, detections[1].id()));
    EXPECT_TRUE(isDeleted(updated, detections[2].id()));
}

TEST_F(Fixture, merge_for_traffic_light)
{
    db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::TrafficLight};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedTrafficLight one {{100, 100, 130, 130}, 0.9};
    const db::eye::DetectedTrafficLight two {{400, 400, 430, 430}, 0.9};

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectTrafficLight,
            db::eye::RecognitionSource::Model,
            1,
            db::eye::DetectedTrafficLights{one, two}
        },
    };

    db::eye::Detections detections {
        {group.id(), db::eye::DetectedTrafficLight{{401, 399, 430, 430}, 0.9}},
        {group.id(), db::eye::DetectedTrafficLight{{200, 200, 230, 230}, 0.9}},
    };

    {   // save  detections
        auto txn = newTxn();
        db::eye::DetectionGateway(*txn).insertx(detections);
        txn->commit();
    }

    const auto [unchanged, updated, _] = makeUpdatedDetections(group, detections, recognitions);

    EXPECT_EQ(unchanged.size(), 1u);
    EXPECT_EQ(updated.size(), 2u);

    EXPECT_TRUE(checkDetectionFromGroup(group, updated));

    EXPECT_TRUE(hasDetectedTrafficLight(updated, one.box));
    EXPECT_TRUE(isDeleted(updated, detections[1].id()));
}

TEST_F(Fixture, merge_for_house_number)
{
    db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::HouseNumber};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedHouseNumber one {{100, 100, 130, 130}, 0.9, "10A"};
    const db::eye::DetectedHouseNumber two {{400, 400, 430, 430}, 0.9, "12"};

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectHouseNumber,
            db::eye::RecognitionSource::Model,
            1,
            db::eye::DetectedHouseNumbers{one, two}
        },
    };

    db::eye::Detections detections {
        {group.id(), db::eye::DetectedHouseNumber{{401, 399, 430, 430}, 0.9, "12"}},
        {group.id(), db::eye::DetectedHouseNumber{{200, 400, 430, 430}, 0.9, "8"}},
    };

    {   // save  detections
        auto txn = newTxn();
        db::eye::DetectionGateway(*txn).insertx(detections);
        txn->commit();
    }

    const auto [unchanged, updated, _] = makeUpdatedDetections(group, detections, recognitions);

    EXPECT_EQ(unchanged.size(), 1u);
    EXPECT_EQ(updated.size(), 2u);

    EXPECT_TRUE(checkDetectionFromGroup(group, updated));

    EXPECT_TRUE(hasDetectedHouseNumber(updated, one.number, one.box));
    EXPECT_TRUE(isDeleted(updated, detections[1].id()));
}

TEST_F(Fixture, merge_for_road_marking)
{
    db::eye::DetectionGroup group{frames[0].id(), db::eye::DetectionType::RoadMarking};

    {   // create group
        auto txn = newTxn();
        db::eye::DetectionGroupGateway(*txn).insertx(group);
        txn->commit();
    }

    const db::eye::DetectedRoadMarking one {
        {100, 100, 130, 130},
        traffic_signs::TrafficSign::RoadMarkingLaneDirectionF,
        0.9
    };

    const db::eye::DetectedRoadMarking two {
        {400, 400, 430, 430},
        traffic_signs::TrafficSign::RoadMarkingLaneDirectionL,
        0.9
    };

    const db::eye::Recognitions recognitions {
        {
            frames[0].id(),
            identical,
            db::eye::RecognitionType::DetectRoadMarking,
            db::eye::RecognitionSource::Model,
            0,
            db::eye::DetectedRoadMarkings{one, two}
        },
    };

    db::eye::Detections detections {
        {
            group.id(), db::eye::DetectedRoadMarking{
                {401, 399, 430, 430},
                traffic_signs::TrafficSign::RoadMarkingLaneDirectionL,
                0.9
            }
        },
        {
            group.id(), db::eye::DetectedRoadMarking{
                {200, 200, 230, 230},
                traffic_signs::TrafficSign::RoadMarkingLaneDirectionR,
                0.9
            }
        },
    };

    {   // save  detections
        auto txn = newTxn();
        db::eye::DetectionGateway(*txn).insertx(detections);
        txn->commit();
    }


    const auto [unchanged, updated, _] = makeUpdatedDetections(group, detections, recognitions);

    EXPECT_EQ(unchanged.size(), 1u);
    EXPECT_EQ(updated.size(), 2u);

    EXPECT_TRUE(checkDetectionFromGroup(group, unchanged));

    EXPECT_TRUE(hasDetectedRoadMarking(updated, one.type, one.box));
    EXPECT_TRUE(isDeleted(updated, detections[1].id()));
}

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