BareGit
#include "user_service.h"

#include <utility>

#include "username.h"

UserService::UserService(DataSourceInterface& data_source)
    : data_source_(data_source)
{}

mw::E<User> UserService::setUsername(
    std::int64_t user_id, const std::string& submitted_username)
{
    auto normalized = normalizeUsername(submitted_username);
    if(!normalized)
    {
        return std::unexpected(mw::httpError(400, "Invalid username"));
    }
    auto transaction = data_source_.beginTransaction();
    if(!transaction)
    {
        return std::unexpected(std::move(transaction.error()));
    }
    auto user = (*transaction)->getUserForUpdate(user_id);
    if(!user)
    {
        return std::unexpected(std::move(user.error()));
    }
    if(!*user)
    {
        return std::unexpected(mw::httpError(404, "User not found"));
    }
    auto updated = (*transaction)->updateUsername(
        user_id, normalized->username, normalized->key);
    if(!updated)
    {
        return std::unexpected(mw::httpError(
            409, "Username is already in use"));
    }
    if(!*updated)
    {
        return std::unexpected(mw::httpError(404, "User not found"));
    }
    auto committed = (*transaction)->commit();
    if(!committed)
    {
        return std::unexpected(std::move(committed.error()));
    }
    (**user).username = std::move(normalized->username);
    return std::move(**user);
}

mw::E<User> UserService::promote(
    std::int64_t administrator_user_id,
    std::int64_t target_user_id)
{
    auto transaction = data_source_.beginTransaction();
    if(!transaction)
    {
        return std::unexpected(std::move(transaction.error()));
    }
    auto administrator = (*transaction)->getUserForUpdate(
        administrator_user_id);
    if(!administrator)
    {
        return std::unexpected(std::move(administrator.error()));
    }
    if(!*administrator ||
       (**administrator).role != UserRole::ADMINISTRATOR)
    {
        return std::unexpected(mw::httpError(403, "Forbidden"));
    }
    auto target = (*transaction)->getUserForUpdate(target_user_id);
    if(!target)
    {
        return std::unexpected(std::move(target.error()));
    }
    if(!*target)
    {
        return std::unexpected(mw::httpError(404, "User not found"));
    }
    if((**target).role == UserRole::ADMINISTRATOR)
    {
        return std::unexpected(mw::httpError(409, "Role cannot be changed"));
    }
    if((**target).role == UserRole::PLAYER)
    {
        auto promoted = (*transaction)->promoteUser(target_user_id);
        if(!promoted)
        {
            return std::unexpected(std::move(promoted.error()));
        }
        if(!*promoted)
        {
            return std::unexpected(mw::httpError(
                409, "User role changed in another request"));
        }
        (**target).role = UserRole::CREATOR;
    }
    auto committed = (*transaction)->commit();
    if(!committed)
    {
        return std::unexpected(std::move(committed.error()));
    }
    return std::move(**target);
}