#include "globals.h"

#include <yandex/maps/wiki/common/extended_xml_doc.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>

#include <yandex/maps/wiki/threadutils/threadpool.h>

#include <chrono>
#include <memory>

namespace maps::wiki::socialsrv {

namespace {

const std::string CORE_DB_ID = "core";
const std::string SOCIAL_DB_ID = "social";
const std::string POOL_ID = "social";

const size_t ACL_CACHE_MAX_SIZE = 10 * 1000;
constexpr std::chrono::seconds ACL_CACHE_EXPIRATION_PERIOD(300);

constexpr std::chrono::minutes USERGROUPS_UIDS_REFRESH_PERIOD(60);

struct GlobalData
{
    explicit GlobalData(const common::ExtendedXmlDoc& config);

    configs::editor::ConfigHolder editorConfig;

    common::PoolHolder corePoolHolder;
    common::PoolHolder socialPoolHolder;
    DbPools dbpools;

    acl_utils::CachingAclChecker aclChecker;
    acl_utils::UserGroupsUidsCache userGroupsUidsCache;
};

void initUserGroupsUidsCache(
    acl_utils::UserGroupsUidsCache& userGroupsUidsCache, pgpool3::Pool& corePool)
{
    const std::vector<acl_utils::UserGroup> groupsToCache{
        acl_utils::UserGroup::Robot,
        acl_utils::UserGroup::Cartographer,
        acl_utils::UserGroup::YandexModerator,
        acl_utils::UserGroup::Outsourcer};

    ThreadPool threadPool{groupsToCache.size()};
    for (const auto& group: groupsToCache) {
        userGroupsUidsCache.try_emplace(group);

        threadPool.push([group, &userGroupsUidsCache, &corePool] {
            userGroupsUidsCache.at(group) =
                std::make_unique<acl_utils::UserGroupUidsCache>(
                    group, corePool, USERGROUPS_UIDS_REFRESH_PERIOD);
        });
    }

    threadPool.shutdown();
}

GlobalData::GlobalData(const common::ExtendedXmlDoc& config)
    : editorConfig(config.get<std::string>("/config/services/editor/config"))

    , corePoolHolder(config, CORE_DB_ID, POOL_ID)
    , socialPoolHolder(config, SOCIAL_DB_ID, POOL_ID)
    , dbpools(corePoolHolder.pool(), socialPoolHolder.pool())

    , aclChecker(corePoolHolder.pool(),
        ACL_CACHE_MAX_SIZE,
        ACL_CACHE_EXPIRATION_PERIOD)
{
    initUserGroupsUidsCache(userGroupsUidsCache, corePoolHolder.pool());
}

std::unique_ptr<GlobalData> g_globalData;

} // namespace

GlobalsScope::GlobalsScope(const common::ExtendedXmlDoc& config)
{
    g_globalData = std::make_unique<GlobalData>(config);
}

GlobalsScope::~GlobalsScope()
{
    g_globalData.reset();
}

const configs::editor::ConfigHolder& Globals::editorConfig()
{
    ASSERT(g_globalData);
    return g_globalData->editorConfig;
}

DbPools& Globals::dbPools()
{
    ASSERT(g_globalData);
    return g_globalData->dbpools;
}

acl_utils::CachingAclChecker& Globals::aclChecker()
{
    ASSERT(g_globalData);
    return g_globalData->aclChecker;
}

acl_utils::UserGroupsUidsCache& Globals::userGroupsUidsCache()
{
    ASSERT(g_globalData);
    return g_globalData->userGroupsUidsCache;
}

} // namespace maps::wiki::socialsrv
