#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_house_number/tests/common.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_house_number/tests/fixture.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_house_number/include/worker.h>

#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_object/include/test_util.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/unit_test/include/frame.h>

#include <library/cpp/testing/unittest/registar.h>
#include <mapreduce/yt/tests/yt_unittest_lib/yt_unittest_lib.h>

#include <deque>

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

std::deque<house_number_sign_detector::HouseNumberSigns> detectionList()
{
    house_number_sign_detector::HouseNumberSigns first {
        {cv::Rect{100, 100, 100,100}, 1.0, 1.0, 1.0, "10A"},
    };

    house_number_sign_detector::HouseNumberSigns second {
        {cv::Rect{100, 100, 100, 100}, 1.0, 1.0, 1.0, "1"},
        {cv::Rect{400, 100, 100, 100}, 1.0, 1.0, 1.0, "2"},
        {cv::Rect{600, 100, 100, 100}, 1.0, 1.0, 1.0, "3"},
    };

    house_number_sign_detector::HouseNumberSigns third {};

    return {first, second, third};
}

std::deque<cv::Mat> maskList()
{
    cv::Mat first = cv::Mat::zeros(1920, 1080, CV_64F);

    cv::Mat second = cv::Mat::zeros(1920, 1080, CV_64F);
    second(cv::Rect{300, 0, 300, 300}) = cv::Mat::ones(300, 300, CV_64F);

    cv::Mat third = cv::Mat::zeros(1080, 1920, CV_64F);

    return {first, second, third};
}

// use traffic light as simple object
struct Detector: public TestDetector<house_number_sign_detector::HouseNumberSign> {
    Detector(): TestDetector(detectionList()) {}
};

struct Segmentator: public TestSegmentator {
    Segmentator(): TestSegmentator(maskList()) {}
};

using Worker = DetectObjectWithFilteringWorker<
    Detector,
    Segmentator,
    MakeHouserNumberRecognition
>;

REGISTER_MAPPER(Worker);

Y_UNIT_TEST_SUITE_F(worker, Fixture)
{

Y_UNIT_TEST(detect_object_with_filtering)
{
    const auto loader = FrameLoader::fromConfig(config());
    const auto yt = NYT::NTesting::CreateTestClient();

    // keep order of frames
    DetectHouseNumberConfig config;
    config.yt.client = yt.Get();
    config.yt.frameLoader = &loader;
    config.yt.rootPath = "//tmp/worker/run";
    config.yt.partitionSize = 10;
    config.yt.concurrency = 1;
    config.yt.useGpu = false;


    const auto recognitions = detectHouseNumberImpl<Worker>(config, {frames[0], frames[1], frames[3]});
    UNIT_ASSERT_EQUAL(recognitions.size(), 3u);

    auto list = detectionList();

    {
        const auto frame = frames.at(0);
        const auto recognition = getRecognition(recognitions, frame);
        const auto detections = list.at(0);

        UNIT_ASSERT_EQUAL(recognition.orientation(), frame.orientation());
        UNIT_ASSERT_EQUAL(recognition.type(), db::eye::RecognitionType::DetectHouseNumber);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto houseNumbers = recognition.value<db::eye::DetectedHouseNumbers>();

        UNIT_ASSERT_EQUAL(houseNumbers.size(), 1u);

        const auto first = detections.at(0);
        UNIT_ASSERT(hasHouseNumber(houseNumbers, first.box, first.number));
    }

    {
        const auto frame = frames.at(1);
        const auto& recognition = getRecognition(recognitions, frame);
        const auto detections = list.at(1);

        UNIT_ASSERT_EQUAL(recognition.orientation(), frame.orientation());
        UNIT_ASSERT_EQUAL(recognition.type(), db::eye::RecognitionType::DetectHouseNumber);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto houseNumbers = recognition.value<db::eye::DetectedHouseNumbers>();

        UNIT_ASSERT_EQUAL(houseNumbers.size(), 2u);

        const auto first = detections.at(0);
        UNIT_ASSERT(hasHouseNumber(houseNumbers, first.box, first.number));

        const auto second = detections.at(2);
        UNIT_ASSERT(hasHouseNumber(houseNumbers, second.box, second.number));
    }

    {
        const auto& frame = frames.at(3);
        const auto& recognition = getRecognition(recognitions, frame);

        UNIT_ASSERT_EQUAL(recognition.orientation(), frame.orientation());
        UNIT_ASSERT_EQUAL(recognition.type(), db::eye::RecognitionType::DetectHouseNumber);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto houseNumbers = recognition.value<db::eye::DetectedHouseNumbers>();

        UNIT_ASSERT_EQUAL(houseNumbers.size(), 0u);
    }
}

} // Y_UNIT_TEST_SUITE

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