#include "fixture.h"

#include <maps/wikimap/mapspro/services/mrc/drive/firmware_updater/libs/db/include/idm/user_role_gateway.h>

#include <maps/infra/tvm2lua/ticket_parser2_lua/lib/lua_api.h>
#include <maps/infra/yacare/include/test_utils.h>
#include <maps/libs/http/include/test_utils.h>
#include <maps/libs/http/include/urlencode.h>
#include <maps/libs/json/include/value.h>

#include <library/cpp/testing/gtest/gtest.h>

namespace maps::fw_updater::storage::tests {

namespace {

const std::string LOGIN = "john";
const std::string IDM_TVM_ID = "2001600";

}

TEST_F(Fixture, test_idm_info)
{
    http::MockRequest request(http::GET,
        http::URL("http://localhost/v2/idm/info/"));
    request.headers[maps::auth::SERVICE_TICKET_HEADER] = "fake";
    request.headers[maps::tvm2::SRC_TVM_ID_HEADER] = IDM_TVM_ID;
    auto response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);

    auto jsonBody = json::Value::fromString(response.body);
    EXPECT_EQ(jsonBody["code"].as<int>(), 0);
    EXPECT_EQ(jsonBody["roles"],
              json::Value::fromFile(SRC_("idm_roles_info.json"))["roles"]);
}

TEST_F(Fixture, test_idm_info_unauthorized)
{
    {
        // Missing SERVICE_TICKET_HEADER
        http::MockRequest request(http::GET,
            http::URL("http://localhost/v2/idm/info/"));
        auto response = yacare::performTestRequest(request);
        EXPECT_EQ(response.status, 401);
    }
    {
        // Non-IDM tvmId
        http::MockRequest request(http::GET,
            http::URL("http://localhost/v2/idm/info/"));
        request.headers[maps::auth::SERVICE_TICKET_HEADER] = "fake";
        request.headers[maps::tvm2::SRC_TVM_ID_HEADER] = "43";
        auto response = yacare::performTestRequest(request);
        EXPECT_EQ(response.status, 403);
    }
}

namespace {

enum class Op { Add, Remove };

http::MockResponse requestChangeUserRole(
    Op operation,
    const std::string& login,
    const std::string& rolePath)
{
    http::MockRequest request(http::POST,
        http::URL(std::string("http://localhost/v2/idm/") +
                  (operation == Op::Add ? "add-role/" : "remove-role/")));
    request.body = "login=" + http::urlEncode(login)
                 + "&path=" + http::urlEncode(rolePath);
    request.headers[maps::auth::SERVICE_TICKET_HEADER] = "fake";
    request.headers[maps::tvm2::SRC_TVM_ID_HEADER] = IDM_TVM_ID;
    return yacare::performTestRequest(request);
}

auto requestAddUserRole(const std::string& login, const std::string& rolePath)
{
    return requestChangeUserRole(Op::Add, login, rolePath);
}

auto requestRemoveUserRole(const std::string& login, const std::string& rolePath)
{
    return requestChangeUserRole(Op::Remove, login, rolePath);
}

} // anonymous namespace

TEST_F(Fixture, test_idm_add_remove_role)
{
    {
        auto response = requestAddUserRole(LOGIN, "project/one/subproject/one-A/role/developer");
        EXPECT_EQ(response.status, 200);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 0);

        auto txn = txnHandle();
        db::idm::UserRoleGateway gtw(*txn);
        auto userRoles = gtw.load(db::idm::table::UserRole::login == LOGIN);
        ASSERT_EQ(userRoles.size(), 1u);
        EXPECT_EQ(userRoles[0].projectRoleId(), 12);
    }
    {
        // Add existing role
        auto response = requestAddUserRole(LOGIN, "project/one/subproject/one-A/role/developer");
        EXPECT_EQ(response.status, 200);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 0);
    }
    {
        auto response = requestRemoveUserRole(LOGIN, "project/one/subproject/one-A/role/developer");
        EXPECT_EQ(response.status, 200);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 0);

        auto txn = txnHandle();
        EXPECT_TRUE(db::idm::UserRoleGateway{*txn}.load().empty());
    }
    {
        auto response = requestRemoveUserRole(LOGIN, "project/one/subproject/one-A/role/developer");
        EXPECT_EQ(response.status, 200);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 0);
    }
}

TEST_F(Fixture, test_idm_invalid_parameters)
{
    {
        // Invalid slug path
        auto response = requestAddUserRole(LOGIN, "odd/elements/number");
        EXPECT_EQ(response.status, 400);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 1);
    }
    {
        // Wrong slug
        auto response = requestAddUserRole(LOGIN, "project/two/wrong/developer");
        EXPECT_EQ(response.status, 400);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 1);
    }
    {
        // Wrong project
        auto response = requestAddUserRole(LOGIN, "project/wrong/role/developer");
        EXPECT_EQ(response.status, 400);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 1);
    }
    {
        // Wrong role
        auto response = requestAddUserRole(LOGIN, "project/two/role/wrong");
        EXPECT_EQ(response.status, 400);
        auto body = json::Value::fromString(response.body);
        EXPECT_EQ(body["code"].as<int>(), 1);
    }
}

TEST_F(Fixture, test_idm_get_all_roles)
{
    const std::string JOHN = "john";
    const std::string PAUL = "paul";

    requestAddUserRole(JOHN, "project/one/subproject/one-A/role/developer");
    requestAddUserRole(JOHN, "project/one/subproject/one-B/role/developer");
    requestAddUserRole(JOHN, "project/two/role/manager");
    requestAddUserRole(PAUL, "project/one/subproject/one-A/role/developer");
    requestAddUserRole(PAUL, "project/two/role/developer");

    http::MockRequest request(http::GET,
        http::URL("http://localhost/v2/idm/get-all-roles/"));
    request.headers[maps::auth::SERVICE_TICKET_HEADER] = "fake";
    request.headers[maps::tvm2::SRC_TVM_ID_HEADER] = IDM_TVM_ID;
    auto response = yacare::performTestRequest(request);
    EXPECT_EQ(response.status, 200);

    auto jsonBody = json::Value::fromString(response.body);
    EXPECT_EQ(jsonBody, json::Value::fromFile(SRC_("get_all_roles_response.json")));
}

} // namespace maps::fw_updater::storage::tests
