#include "message_collect_server_info.h"
#include "stream_data.h"

#include <saas/rtyserver/model/index.h>

#include <util/generic/cast.h>

namespace {
    void InitRps(TMap<ui32, float>& rps) {
        rps[1] = 0.0f;
        rps[3] = 0.0f;
        rps[10] = 0.0f;
        rps[30] = 0.0f;
    }
}

NJson::TJsonValue TMessageCollectServerInfo::TIndexInfo::GetJsonInfo() const {
    NJson::TJsonValue map;
    if (Dir.Defined())
        map.InsertValue("dir", *Dir);
    if (Type.Defined())
        map.InsertValue("type", *Type);
    if (Realm.Defined())
        map.InsertValue("realm", *Realm);
    if (Count.Defined())
        map.InsertValue("count", *Count);
    if (CountWithnoDel.Defined())
        map.InsertValue("count_withnodel", *CountWithnoDel);
    if (CountSearchable.Defined())
        map.InsertValue("count_searchable", *CountSearchable);
    if (HasSearcher.Defined())
        map.InsertValue("has_searcher", *HasSearcher);
    if (Cache.Defined())
        map.InsertValue("cache", *Cache);
    if (Timestamp.Defined())
        map.InsertValue("timestamp", *Timestamp);
    if (AddCount.Defined())
        map.InsertValue("add_count", *AddCount);
    if (DelCount.Defined())
        map.InsertValue("del_count", *DelCount);
    if (UpdCount.Defined())
        map.InsertValue("upd_count", *UpdCount);
    if (LockedMemorySizeInBytes.Defined())
        map.InsertValue("mlocked_size", *LockedMemorySizeInBytes);
    if (IndexSizeInBytes.Defined())
        map.InsertValue("size", *IndexSizeInBytes);


    return map;
}

bool TMessageCollectServerInfo::IsAllCachesUsage() const {
    for (const auto& [_, value]: IndexInfo) {
        if (value.Cache.Get() == nullptr) {
            return false;
        }
    }
    return true;
}
NJson::TJsonValue TMessageCollectServerInfo::GetIndexerClientsCountInfo() const
{
    NJson::TJsonValue result(NJson::JSON_MAP);
    for (TMap<TString, unsigned>::const_iterator i = IndexerClientsCount.begin(), e = IndexerClientsCount.end(); i != e; ++i)
        result.InsertValue(i->first + "_cl", i->second);
    for (TMap<TString, unsigned>::const_iterator i = IndexerConnectionsCount.begin(), e = IndexerConnectionsCount.end(); i != e; ++i)
        result.InsertValue(i->first + "_conn", i->second);
    for (TMap<TString, unsigned>::const_iterator i = IndexerRequestsCount.begin(), e = IndexerRequestsCount.end(); i != e; ++i)
        result.InsertValue(i->first + "_req", i->second);

    return result;
}

void TMessageCollectServerInfo::Prepare()
{
    DiskQueueSize = 0;
    MemoryQueueSize = 0;
    MergerTasks = 0;
    RepairerTasks = 0;
    MergerTasksInfo.SetType(NJson::JSON_ARRAY);
    InitRps(DiskIndexRps);
    InitRps(MemoryIndexRps);
    InitRps(HttpSearchRps);
    InitRps(BaseSearchRps);
    InitRps(NehSearchRps);
    InitRps(FailedSearchRps);
    DocsInDiskIndexers = 0;
    DocsInFinalIndexes = 0;
    DocsInMemoryIndexes = 0;
    LockedMemorySizeInBytes = 0;
    SearchableDocs = 0;
    MemoryIndexersCount = 0;
    ClosingIndexers = 0;
    DefaultKps = 0;
    SearchSourcesCount = 0;
    Status = "";
    MergerStatus = tsStopped;
}

void TMessageCollectServerInfo::AddIndexInfo(const IIndexController& info)
{
    TGuard<TMutex> g(Mutex);
    const TString& indexDirectory = info.Directory().BaseName();
    IndexInfo[indexDirectory].Count = info.GetDocumentsCount();
    IndexInfo[indexDirectory].CountWithnoDel = info.GetDocumentsCount(false);
    IndexInfo[indexDirectory].CountSearchable = info.GetSearchableDocumentsCount();
    IndexInfo[indexDirectory].HasSearcher = info.IsSearchSource();
    IndexInfo[indexDirectory].Timestamp = SafeIntegerCast<ui64>(info.GetTimestamp().Get());
    IndexInfo[indexDirectory].Type = ToString(info.GetType());
    IndexInfo[indexDirectory].Realm = ToString(info.GetRealm());
    IndexInfo[indexDirectory].Dir = info.Directory().PathName();
    IndexInfo[indexDirectory].LockedMemorySizeInBytes = info.GetLockedMemorySize();
    IndexInfo[indexDirectory].IndexSizeInBytes = info.GetFilesSize();
    IndexTimestamp = Max<ui64>(IndexTimestamp, *IndexInfo[indexDirectory].Timestamp);
}

NJson::TJsonValue TMessageCollectServerInfo::GetBallocOptionsInfo() const {
    NJson::TJsonValue result(NJson::JSON_MAP);

    result.InsertValue("SoftLimit", NAllocSetup::GetSoftLimit());
    result.InsertValue("HardLimit", NAllocSetup::GetHardLimit());
    result.InsertValue("AllocationThreshold", NAllocSetup::GetAllocationThreshold());
    result.InsertValue("SoftReclaimDivisor", NAllocSetup::GetSoftReclaimDivisor());
    result.InsertValue("AngryReclaimDivisor", NAllocSetup::GetAngryReclaimDivisor());

    return result;
}

unsigned TMessageCollectServerInfo::GetDiskQueueSize() const
{
    TGuard<TMutex> g(Mutex);
    return DiskQueueSize;
}

void TMessageCollectServerInfo::Fill(TServerInfo& info)
{
    TCollectServerInfo::Fill(info);
    info("queue_disk", GetDiskQueueSize())
        ("queue_mem", GetMemoryQueueSize())
        ("merger_tasks", GetMergerTasks())
        ("repair_tasks", GetRepairerTasks())
        ("uptime", ToString(GetUptime()))
        ("server_status", GetStatus())
        ("disk_index_rps", *GetDiskIndexRps())
        ("memory_index_rps", *GetMemoryIndexRps())
        ("search_rps_http", *GetHttpSearchRps())
        ("search_rps_neh", *GetNehSearchRps())
        ("search_rps_neh_base", *GetBaseSearchRps())
        ("search_rps_failed", *GetFailedSearchRps())
        ("docs_in_disk_indexers", GetDocsInDiskIndexers())
        ("docs_in_final_indexes", GetDocsInFinalIndexes())
        ("docs_in_memory_indexes", GetDocsInMemoryIndexes())
        ("searchable_docs", GetSearchableDocs())
        ("closing_indexers", GetClosingIndexers())
        ("memory_searchers_count", GetMemoryIndexersCount())
        ("default_kps", GetDefaultKps())
        ("search_sources_count", GetSearchSourcesCount())
        ("active_contexts", GetActiveContexts())
        ("active_repliers", GetActiveRepliers())
        ("indexer_clients", GetIndexerClientsCountInfo())
        ("caches_state", GetCachesStateInfo())
        ("indexes", GetIndexesInfo())
        ("index_timestamp", IndexTimestamp)
        ("index_age", Seconds() - IndexTimestamp)
        ("factors_count", GetFactorsCount())
        ("factors_info", GetFactorsConfig())
        ("merger_tasks_info", MergerTasksInfo)
        ("merger_status", ToString(GetMergerStatus()))
        ("files_size", FilesSizeInfo.GetReport(IsHumanReadable))
        ("add_docs", InfoAddCount)
        ("delete_docs", InfoDeleteCount)
        ("update_docs", InfoUpdateCount)
        ("archive_info", GetFullArcInfo())
        ("locked_memory_size", GetLockedMemorySizeInBytes())
        ("persistent_search_ban", PersistentSearchBan)
        ("slot_info_search_ban", SlotInfoSearchBan)
        ("docfetcher_search_ban", DocfetcherSearchBan)
        ("search_enabled", SearchEnabled)
        ("indexing_enabled", IndexingEnabled)
        ("current_sync_resource", CurrentSyncResource)
        ("last_sync_resource", LastSyncResource)
        ("balloc_options", GetBallocOptionsInfo())
        ;

    if (GetFactorsCount() * GetFactorsUsage().GetMap().size() < 10000) {
        info("used_factors", GetFactorsUsage());
    } else {
        info("used_factors_omitted_as_big", true);
    }

    NJson::TJsonValue components(NJson::JSON_MAP);
    for (auto&& i : ComponentsInfo) {
        components[i.first] = i.second->Serialize();
    }

    info("components", components);
}

NJson::TJsonValue TMessageCollectServerInfo::GetCachesStateInfo() {
    NJson::TJsonValue caches;
    for (const auto& [key, value]: IndexInfo) {
        if (value.Cache) {
            caches.InsertValue(key, *value.Cache);
        }
    }
    return caches;
}

NJson::TJsonValue TMessageCollectServerInfo::GetIndexesInfo() {
    NJson::TJsonValue indexes;
    for (const auto& [key, value]: IndexInfo) {
        indexes.InsertValue(key, value.GetJsonInfo());
    }
    return indexes;
}

void TMessageCollectServerInfo::SetCachesStates(const TMap<TString, bool>& cachesStates) {
    for (const auto& [key, value]: cachesStates) {
        IndexInfo[key].Cache = value;
    }
}
