#include "fixture.h"

#include <maps/infra/yacare/include/test_utils.h>
#include <maps/infra/yacare/include/yacare.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/disqualified_source_gateway.h>
#include <yandex/maps/geolib3/sproto.h>
#include <yandex/maps/mrc/signal_queue/result_queue.h>
#include <yandex/maps/mrc/signal_queue/results.h>
#include <yandex/maps/proto/common2/geometry.sproto.h>
#include <yandex/maps/proto/common2/response.sproto.h>
#include <yandex/maps/proto/mrc/common/common.sproto.h>
#include <yandex/maps/proto/mrc/ugc/targets.sproto.h>
#include <yandex/maps/proto/mrc/ugc/ugc.sproto.h>
#include <yandex/maps/proto/offline-mrc/results.sproto.h>

using namespace std::chrono_literals;

namespace pgeometry = yandex::maps::sproto::common2::geometry;
namespace presponse = yandex::maps::sproto::common2::response;
namespace ptargets = yandex::maps::sproto::mrc::ugc::targets;
namespace pcommon = yandex::maps::sproto::mrc::common;
namespace pugc = yandex::maps::sproto::mrc::ugc;

using namespace yacare::tests;

namespace maps::mrc::agent_proxy::tests {

namespace {

const std::string USER_ID = "111111111";
const std::string SOURCE_ID = "source";

http::MockRequest makeTilesRequest(int tileX, int tileY, int tileZ)
{
    http::MockRequest request(
        http::GET,
        http::URL("http://localhost/ugc/targets/tiles")
            .addParam("deviceid", SOURCE_ID)
            .addParam("uid", USER_ID)
            .addParam("x", tileX)
            .addParam("y", tileY)
            .addParam("z", tileZ)
    );

    return request;
}

bool equalPolylines(const geolib3::Polyline2& lhs, const geolib3::Polyline2& rhs)
{
    return lhs.points() == rhs.points();
}

void checkResponsePolylines(
    const presponse::Response& response,
    const std::vector<geolib3::Polyline2>& expectedPolylines)
{
    EXPECT_TRUE(response.reply().defined());
    const auto& rootGeoObject = *response.reply();
    ASSERT_EQ(rootGeoObject.geo_object().size(), expectedPolylines.size());

    for (size_t i = 0; i < rootGeoObject.geo_object().size(); ++i) {
        const auto& geoObj = rootGeoObject.geo_object()[i];

        EXPECT_EQ(geoObj.metadata().size(), 1u);
        const auto& metadata = geoObj.metadata()[0];
        const auto& targetMetadata = metadata[pugc::TARGET_METADATA];
        EXPECT_TRUE(targetMetadata.defined());
        EXPECT_TRUE(targetMetadata->oneway());
        EXPECT_EQ(targetMetadata->passing_direction(), pugc::Direction::FORWARD);
        EXPECT_TRUE(targetMetadata->traffic() == pugc::Traffic::RIGHT_HAND);

        EXPECT_EQ(geoObj.geometry().size(), 1u);
        const auto& geometry = geoObj.geometry()[0];
        EXPECT_TRUE(geometry.polyline().defined());
        auto polyline = geolib3::sproto::decode(*geometry.polyline());

        EXPECT_TRUE(equalPolylines(polyline, expectedPolylines[i]));
    }
}

} // namespace

TEST_F(Fixture, tile_targets_empty_response_test)
{
    // No roads in this tile
    auto request = makeTilesRequest(158368, 82085, 18);
    auto response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);

    auto protoResponse = boost::lexical_cast<presponse::Response>(response.body);
    checkResponsePolylines(protoResponse, {});

    request = makeTilesRequest(158368, 82085, 18);
    request.url.addParam("camera-direction", "right");
    response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);

    protoResponse = boost::lexical_cast<presponse::Response>(response.body);
    checkResponsePolylines(protoResponse, {});

    request = makeTilesRequest(2475, 1284, 12);
    response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 400);
}

TEST_F(Fixture, tile_targets_test)
{
    // For camera-direction = front there are 3 uncovered edges
    auto request = makeTilesRequest(158370, 82087, 18);
    auto response = yacare::performTestRequest(request);
    ASSERT_EQ(response.status, 200);

    auto protoResponse = boost::lexical_cast<presponse::Response>(response.body);

    checkResponsePolylines(
        protoResponse,
        {
            geolib3::Polyline2(geolib3::PointsVector{{37.489471, 55.825890}, {37.488836, 55.825923}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488672, 55.825531}, {37.488674, 55.825548}, {37.488836, 55.825923}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488836, 55.825923}, {37.488674, 55.825548}, {37.488672, 55.825531}})
        });

    // For camera-direction = right all 6 edges are uncovered
    request = makeTilesRequest(158370, 82087, 18);
    request.url.addParam("camera-direction", "right");
    response = yacare::performTestRequest(request);
    ASSERT_EQ(response.status, 200);
    EXPECT_TRUE(response.headers.find("ETag") != response.headers.end());

    protoResponse = boost::lexical_cast<presponse::Response>(response.body);

    checkResponsePolylines(
        protoResponse,
        {
            geolib3::Polyline2(geolib3::PointsVector{{37.488098, 55.825961}, {37.488836, 55.825923}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488836, 55.825923}, {37.488098, 55.825961}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488836, 55.825923}, {37.489471, 55.825890}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.489471, 55.825890}, {37.488836, 55.825923}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488672, 55.825531}, {37.488674, 55.825548}, {37.488836, 55.825923}}),
            geolib3::Polyline2(geolib3::PointsVector{{37.488836, 55.825923}, {37.488674, 55.825548}, {37.488672, 55.825531}})
        });

    // For disqualified source
    {
        auto txn = pgPool().masterWriteableTransaction();
        auto now = chrono::TimePoint::clock::now();
        auto disq = db::DisqualifiedSource{db::DisqType::DisableCapturing,
                                           SOURCE_ID,
                                           now - chrono::Days{7},
                                           now + chrono::Days{7}};
        db::DisqualifiedSourceGateway{*txn}.insert(disq);
        txn->commit();
    }
    request = makeTilesRequest(158370, 82087, 18);
    response = yacare::performTestRequest(request);
    ASSERT_EQ(response.status, 200);
    EXPECT_TRUE(response.headers.find("ETag") != response.headers.end());

    protoResponse = boost::lexical_cast<presponse::Response>(response.body);

    checkResponsePolylines(protoResponse, {});
}

TEST_F(Fixture, targets_version_test)
{
    http::MockRequest request(
        http::GET,
        http::URL("http://localhost/ugc/targets/version")
            .addParam("deviceid", SOURCE_ID)
            .addParam("uid", USER_ID)
    );

    auto response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);

    auto protoResponse = boost::lexical_cast<ptargets::Version>(response.body);
    EXPECT_EQ(protoResponse.value(),
              Playground::instance().graphCoverage.version());
}

} // namespace maps::mrc::agent_proxy::tests
