#include "common.h"
#include "fixture.h"

#include <maps/infra/yacare/include/test_utils.h>
#include <maps/infra/yacare/include/yacare.h>
#include <maps/libs/http/include/http.h>
#include <maps/libs/http/include/test_utils.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/ugc/gateway.h>
#include <yandex/maps/mds/mds.h>

#include <boost/lexical_cast.hpp>

#include <chrono>
#include <optional>

namespace maps {
namespace mrc {
namespace tasks_planner {
namespace tests {

TEST(assignemnts_api_should, test_get_assignments)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    fixture.fillAssignmentReview(assignment.id());

    http::MockRequest request(http::GET,
        http::URL("http://localhost/assignments/")
            .addParam("lang", "ru_RU")
    );
    auto response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);
    EXPECT_EQ(response.headers["X-Total-Count"], "1");

    auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
    validateJson(response.body,
                 responseSchema.at({"GET", "/assignments/", 200}),
                 schemasDir());
}

TEST(assignemnts_api_should, test_get_assignment_by_id)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    fixture.fillAssignmentReview(assignment.id());

    http::MockRequest request(
        http::GET,
        http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/")
            .addParam("lang", "ru_RU")
    );

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

    auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
    validateJson(response.body,
                responseSchema.at({"GET", "/assignments/{id}/", 200}),
                schemasDir());
    auto bodyJson = json::Value::fromString(response.body);
    EXPECT_EQ(bodyJson["fulfillmentInfoByDirections"].size(), 1u);
}


TEST(assignemnts_api_should, test_patch_task_by_id)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    fixture.fillAssignmentReview(assignment.id());

    {
        http::MockRequest request(
            http::PATCH,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/")
                .addParam("lang", "ru_RU")
        );

        request.body = "\"Resolved\"";
        auto response = yacare::performTestRequest(request);
        EXPECT_EQ(response.status, 200);

        auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
        validateJson(response.body,
                    responseSchema.at({"PATCH", "/assignments/{id}/", 200}),
                    schemasDir());
        auto modifiedAssignment = db::ugc::AssignmentGateway(*fixture.txnHandle())
            .loadById(assignment.id());
        EXPECT_EQ(modifiedAssignment.status(), db::ugc::AssignmentStatus::Completed);
    }

    {
        http::MockRequest request(
            http::PATCH,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/")
                .addParam("lang", "ru_RU")
        );
        request.body = "\"Closed\"";

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


TEST(assignemnts_api_should, test_get_assignment_tracks)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    fixture.fillAssignmentReview(assignment.id());


    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/tracks/")
        );

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

        auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
        auto body = response.body;
        validateJson(body,
                    responseSchema.at({"GET", "/assignments/{id}/tracks/", 200}),
                    schemasDir());
        auto bodyJson = json::Value::fromString(body);
        EXPECT_THAT(bodyJson["coveredTargets"]["coordinates"].size(), testing::Gt(0u));
        EXPECT_THAT(bodyJson["uncoveredTargets"]["coordinates"].size(), testing::Gt(0u));
        EXPECT_THAT(bodyJson["track"]["coordinates"].size(), testing::Gt(0u));
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/tracks/")
                .addParam("camera_direction", "Right")
        );

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

        auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
        auto body = response.body;
        validateJson(body,
                    responseSchema.at({"GET", "/assignments/{id}/tracks/", 200}),
                    schemasDir());
        auto bodyJson = json::Value::fromString(body);
        EXPECT_EQ(bodyJson["coveredTargets"]["coordinates"].size(), 0u)
            << "Wrong 'coveredTargets' in '" << body << "'";
        EXPECT_THAT(bodyJson["uncoveredTargets"]["coordinates"].size(), testing::Gt(0u));
        EXPECT_EQ(bodyJson["track"]["coordinates"].size(), 0u);
    }
}

TEST(assignemnts_api_should, test_get_assignment_tracks_without_review)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);

    http::MockRequest request(
        http::GET,
        http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/tracks/")
    );

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

    auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
    validateJson(response.body,
                 responseSchema.at({"GET", "/assignments/{id}/tracks/", 200}),
                 schemasDir());
}


TEST(assignemnts_api_should, test_get_assignment_photos)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    fixture.createAssignmentPhoto(assignment.id(), 0.9 /* quality */, db::CameraDeviation::Front, "2017-10-02 00:00:00");
    fixture.createAssignmentPhoto(assignment.id(), std::nullopt /* quality */, db::CameraDeviation::Right, "2017-10-03 00:00:00");

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
        );

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

        auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
        validateJson(response.body,
                    responseSchema.at({"GET", "/assignments/{id}/photos/", 200}),
                    schemasDir());

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 2u);
    }

    {
        const std::string NEW_HOST = "external_host.yandex.ru";
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("rewrite_host", NEW_HOST)
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 2u);
        EXPECT_TRUE(response.body.find(NEW_HOST) != std::string::npos);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("results", "0")
        );

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

        auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
        validateJson(response.body,
                    responseSchema.at({"GET", "/assignments/{id}/photos/", 200}),
                    schemasDir());

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 0u);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("camera_directions", "Front")
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 1u);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("camera_directions", "Front,Right")
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 2u);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("camera_directions", "Wrong")
        );

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

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("taken_at_before", "Wrong")
        );

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

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("taken_at_after", "2017-10-02T00:00:00")
                .addParam("taken_at_before", "2017-10-04T00:00:00")
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 2u);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("taken_at_after", "2017-10-03T00:00:00")
                .addParam("taken_at_before", "2017-10-04T00:00:00")
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 1u);
    }

    {
        http::MockRequest request(
            http::GET,
            http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/photos/")
                .addParam("taken_at_after", "2017-10-02T00:00:00")
                .addParam("taken_at_before", "2017-10-03T00:00:00")
        );

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

        auto responseJson = maps::json::Value::fromString(response.body);
        EXPECT_EQ(responseJson.size(), 1u);
    }
}


TEST(assignemnts_api_should, test_get_assignment_objects)
{
    Fixture fixture;
    const db::UserId USER = "41152097";
    auto tasksGroup = fixture.createTasksGroup();
    auto task = fixture.createTask(tasksGroup.id());
    auto assignment = fixture.createAssignment(task, USER);
    auto aobjects = fixture.createAssignmentObjects(assignment.id());

    http::MockRequest request(
        http::GET,
        http::URL("http://localhost/assignments/" + std::to_string(assignment.id()) + "/objects/")
    );

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

    auto responseSchema = readResponseSchemasFromSwagger(schemasPath());
    validateJson(response.body,
                 responseSchema.at({"GET", "/assignments/{id}/objects/", 200}),
                 schemasDir());
}

} // namespace tests
} // namespace tasks_planner
} // namespace mrc
} // namespace maps
