#include <library/cpp/testing/gtest/gtest.h>
#include <maps/libs/http/include/test_utils.h>
#include <maps/wikimap/mapspro/libs/unittest/include/yandex/maps/wiki/unittest/json_schema.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/feedback/include/push_feedback.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/location/include/rotation.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/unit_test/include/frame.h>

using namespace maps::mrc::db::eye;

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

namespace {

static const std::string TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%S";

HypothesisContext::Item makeSignContextItem(
    traffic_signs::TrafficSign signType,
    db::FeaturePrivacy privacy)
{
    return {
        db::eye::Object(
            db::TId(0),
            db::eye::SignAttrs{signType, false}
        ),
        db::eye::ObjectLocation(
            db::TId(0u),
            geolib3::Point2(0., 0.),
            toRotation(geolib3::Heading(0.0), identical)
        ),
        db::eye::Detection(
            db::TId(0),
            db::eye::DetectedSign{
                {100, 100, 200, 200},
                signType,
                0.9,
                false,
                0.95
            }
        ),
        db::eye::DetectionGroup(
            db::TId(0),
            db::eye::DetectionType::Sign
        ),
        db::eye::Frame(
            db::TId(0),
            identical,
            makeUrlContext(1, "cw0"),
            {1920, 1080},
            time()
        ),
        db::eye::FrameLocation(
            db::TId(0u),
            geolib3::Point2(0., 0.),
            toRotation(geolib3::Heading(0.0), identical)
        ),
        db::eye::FramePrivacy(
            db::TId(0), privacy
        )
    };
}

} // namespace

TEST(push_feedback_tests, push_success_test)
{
    db::eye::Hypothesis hypothesis(
        geolib3::Point2(60.64779074517774, 56.78799602242654),
        db::eye::AbsentParkingAttrs()
    );
    HypothesisContext context({
        {
            db::eye::Object(
                db::TId(0),
                db::eye::SignAttrs{traffic_signs::TrafficSign::InformationParking, false}
            ),
            db::eye::ObjectLocation(
                db::TId(0u),
                geolib3::Point2(37.6617198, 55.8345156),
                toRotation(geolib3::Heading(220.55), identical)
            ),
            db::eye::Detection(
                db::TId(0),
                db::eye::DetectedSign{
                    {1204, 628, 1255, 679},
                    traffic_signs::TrafficSign::InformationParking,
                    0.9,
                    false,
                    0.95
                }
            ),
            db::eye::DetectionGroup(
                db::TId(0),
                db::eye::DetectionType::Sign
            ),
            db::eye::Frame(
                db::TId(0),
                identical,
                makeUrlContext(8793594, "cw0"),
                {1600, 1200},
                chrono::parseIntegralDateTime("2020-09-23T10:54:52", TIMESTAMP_FORMAT)
            ),
            db::eye::FrameLocation(
                db::TId(0u),
                geolib3::Point2(37.6617198, 55.8345156),
                toRotation(geolib3::Heading(220.55), identical)
            ),
            db::eye::FramePrivacy(
                db::TId(0), db::FeaturePrivacy::Public
            )
        }
    });

    FrameUrlResolver frameUrl(
        "https://mrc-browser.maps.yandex.net",
        "https://mrc-browser-pro.maps.yandex.net"
    );
    FeedbackUrlResolver feedbackUrl(
        "http://www.social.ru"
    );

    http::MockHandle pushFeedbackMockHandle = http::addMock(
        "http://www.social.ru/feedback/tasks/parking",
        [&](const http::MockRequest& request) {

        const TString schemaPath = ArcadiaSourceRoot() +
            "/maps/wikimap/mapspro/schemas/social/social.post.parking_feedback_task.request.schema.json";
        wiki::unittest::validateJson(request.body, schemaPath);

        const TString resultPath = ArcadiaSourceRoot() +
            "/maps/wikimap/mapspro/services/mrc/eye/lib/feedback/tests/json/absent_parking.json";
        EXPECT_EQ(json::Value::fromString(request.body), json::Value::fromFile(resultPath));

            return http::MockResponse(
                R"(
                    {
                        "feedbackTask" : {
                            "id" : "123"
                        }
                    }
                )"
            );
        }
    );

    db::TId feedbackId = pushFeedback(hypothesis, context, frameUrl, feedbackUrl);

    EXPECT_EQ(feedbackId, 123);
}

TEST(push_feedback_tests, client_error_test)
{
    db::eye::Hypothesis hypothesis(
        geolib3::Point2(0, 0),
        db::eye::AbsentParkingAttrs()
    );
    HypothesisContext context({
        makeSignContextItem(
            traffic_signs::TrafficSign::InformationParking,
            db::FeaturePrivacy::Public
        ),
    });

    FrameUrlResolver frameUrl(
        "http://www.browser.ru",
        "http://www.browser-pro.ru"
    );

    FeedbackUrlResolver feedbackUrl(
        "http://www.social.ru"
    );

    http::MockHandle pushFeedbackMockHandle = http::addMock(
        "http://www.social.ru/feedback/tasks/parking",
        [&](const http::MockRequest& /*request*/) {
            return http::MockResponse::withStatus(400);
        }
    );

    EXPECT_THROW(
        pushFeedback(hypothesis, context, frameUrl, feedbackUrl),
        maps::RuntimeError
    );
}

TEST(push_feedback_tests, server_error_test)
{
    db::eye::Hypothesis hypothesis(
        geolib3::Point2(0, 0),
        db::eye::AbsentParkingAttrs()
    );
    HypothesisContext context({
        makeSignContextItem(
            traffic_signs::TrafficSign::InformationParking,
            db::FeaturePrivacy::Public
        ),
    });

    FrameUrlResolver frameUrl(
        "http://www.browser.ru",
        "http://www.browser-pro.ru"
    );

    FeedbackUrlResolver feedbackUrl(
        "http://www.social.ru"
    );

    http::MockHandle pushFeedbackMockHandle = http::addMock(
        "http://www.social.ru/feedback/tasks/parking",
        [&](const http::MockRequest& /*request*/) {
            return http::MockResponse::withStatus(500);
        }
    );

    EXPECT_THROW(
        pushFeedback(hypothesis, context, frameUrl, feedbackUrl),
        maps::common::RetryNumberExceeded
    );
}

TEST(push_feedback_tests, throw_with_empty_context_test)
{
    db::eye::Hypothesis hypothesis(
        geolib3::Point2(0, 0),
        db::eye::AbsentParkingAttrs()
    );
    HypothesisContext context({});

    FrameUrlResolver frameUrl(
        "http://www.browser.ru",
        "http://www.browser-pro.ru"
    );

    FeedbackUrlResolver feedbackUrl(
        "http://www.social.ru"
    );

    EXPECT_THROW(
        pushFeedback(hypothesis, context, frameUrl, feedbackUrl),
        maps::Exception
    );
}

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