#include <spdlog/spdlog.h>
#include "server_internal.hxx"

#define check_admin_response() check_user_response(); if (!user->admin) return { .e = "Forbidden" };
#define check_admin_optional() check_user_optional(); if (!user->admin) return "Forbidden";

// TODO Log admin action

mrpc::Response<std::vector<mrpc::UserInfo>> Server::Admin_list_users(std::string &&token) {
    check_admin_response();
    {
        std::shared_lock lock{user_lock};
        std::vector<mrpc::UserInfo> info;
        info.reserve(users.size());
        for (const auto &us : users) {
            const auto u = us.second.get();
            info.push_back(mrpc::UserInfo {
                .id = u->id,
                .name = u->name,
                .tfa = u->tfa_enabled,
                .admin = u->admin,
                .enabled = u->enabled
            });
        }
        return { .o = std::move(info) };
    }
}

std::optional<std::string> Server::Admin_delete_user(std::string &&token, std::uint64_t &&user_id) {
    check_admin_optional();
    auto target = get_user(user_id);
    if (!target) return "Invalid user";
    delete_user(target);
    save();
    return std::nullopt;
}

std::optional<std::string> Server::Admin_logout(std::string &&token, std::uint64_t &&user_id) {
    check_admin_optional();
    logout_user(user_id);
    return std::nullopt;
}

std::optional<std::string> Server::Admin_disable_tfa(std::string &&token, std::uint64_t &&user_id) {
    check_admin_optional();
    auto u = get_user(user_id);
    if (u) u->tfa_enabled = false;
    save();
    return std::nullopt;
}

std::optional<std::string> Server::Admin_set_admin(std::string &&token, std::uint64_t &&user_id, bool &&admin) {
    check_admin_optional();
    auto u = get_user(user_id);
    if (u) u->admin = admin;
    save();
    return std::nullopt;
}

std::optional<std::string> Server::Admin_set_enabled(std::string &&token, std::uint64_t &&user_id, bool &&enabled) {
    check_admin_optional();
    auto u = get_user(user_id);
    if (u) u->enabled = enabled;
    save();
    return std::nullopt;
}

std::optional<std::string> Server::Admin_sudo(std::string &&token, std::uint64_t &&user_id) {
    check_admin_optional();
    auto u = get_user(user_id);
    if (!u)
        return "Invalid user";
    {
        std::unique_lock tlock{token_lock};
        auto &t = tokens.at(token);
        t->sudo_original_user = user;
        t->user = u;
        t->refresh();
    }
    return std::nullopt;
}

std::optional<std::string> Server::Admin_unsudo(std::string &&token) {
    check_user_optional();
    {
        std::unique_lock lock{token_lock};
        auto &t = tokens.at(token);
        if (t->sudo_original_user == nullptr)
            return "Unauthorized";
        t->user = t->sudo_original_user;
        t->sudo_original_user = nullptr;
        t->refresh();
    }
    return std::nullopt;
}

std::optional<std::string> Server::Admin_shutdown(std::string &&token) {
    check_admin_optional();
    spdlog::info("Received rpc shutdown request from admin user {}", user->name);
    g_service->stop();
    return std::nullopt;
}
