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

#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_object/include/test_util.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<signdetect::DetectedSigns> detectionList()
{
    signdetect::DetectedSigns first {
        {
            cv::Rect{100, 100, 100,100},
            traffic_signs::TrafficSign::ProhibitoryNoParking, 1.0,
            traffic_signs::TemporarySign::No, 0.99,
            "", 1.0
        },
    };

    signdetect::DetectedSigns second {
        {
            cv::Rect{100, 100, 100,100},
            traffic_signs::TrafficSign::ProhibitoryNoParking, 1.0,
            traffic_signs::TemporarySign::No, 0.99,
            "", 0.0
        },
        {
            cv::Rect{400, 100, 100, 100},
            traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed70, 0.98,
            traffic_signs::TemporarySign::No, 0.99,
            "70", 1.0
        },
        {
            cv::Rect{600, 100, 100, 100},
            traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed40, 1.0,
            traffic_signs::TemporarySign::Yes, 0.89,
            "", 0.0
        },
    };

    signdetect::DetectedSigns 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<signdetect::DetectedSign> {
    Detector(): TestDetector(detectionList()) {}
};

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

chrono::TimePoint time()
{
    static auto global = chrono::TimePoint::clock::now();

    const auto now = global;
    global += std::chrono::seconds(1);

    return now;
}

inline db::eye::MrcUrlContext makeUrlContext(db::TId id, const std::string& mdsPath)
{
    return db::eye::MrcUrlContext{id, unittest::MDS_GROUP_ID, mdsPath};
}

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

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
    DetectSignConfig 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 = detectSignImpl<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::DetectSign);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto signs = recognition.value<db::eye::DetectedSigns>();

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

        const auto first = detections.at(0);
        UNIT_ASSERT(hasSign(signs, first.box, traffic_signs::TrafficSign::ProhibitoryNoParking, false));
    }

    {
        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::DetectSign);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto signs = recognition.value<db::eye::DetectedSigns>();

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

        const auto first = detections.at(0);
        UNIT_ASSERT(hasSign(signs, first.box, traffic_signs::TrafficSign::ProhibitoryNoParking, false));

        const auto second = detections.at(2);
        UNIT_ASSERT(hasSign(signs, second.box, traffic_signs::TrafficSign::ProhibitoryEofMaxSpeed40, true));
    }

    {
        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::DetectSign);
        UNIT_ASSERT_EQUAL(recognition.source(), db::eye::RecognitionSource::Model);

        const auto signs = recognition.value<db::eye::DetectedSigns>();

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

} // Y_UNIT_TEST_SUITE

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