#include "info.h"
#include "messages.h"

#include <saas/util/proc_status/status.h>
#include <saas/util/system/environment.h>

#include <library/cpp/logger/global/global.h>

#include <library/cpp/build_info/build_info.h>
#include <library/cpp/cpuload/cpu_load.h>
#include <library/cpp/svnversion/svnversion.h>
#include <library/cpp/json/json_reader.h>

#include <util/generic/set.h>
#include <util/generic/vector.h>
#include <util/string/vector.h>
#include <util/string/strip.h>
#include <util/system/progname.h>
#include <util/system/mem_info.h>
#include <util/system/info.h>
#include <util/folder/filelist.h>
#include <util/stream/file.h>
#include <util/system/execpath.h>

struct TSlotsInfo {
    TSlotsInfo() {
        TFsPath path = "dump.json";
        if (!path.Exists())
            return;
        TUnbufferedFileInput fi(path);
        NJson::ReadJsonTree(&fi, &Dump);
    }
    static const TSlotsInfo Instance;
    NJson::TJsonValue Dump;
};

const TSlotsInfo TSlotsInfo::Instance;

TServerInfo GetCommonServerInfo() {
    const TVector<TString>& svnInfo = SplitString(GetProgramSvnVersion(), "\n");
    TMap<TString, TString> infoMap;
    for (TVector<TString>::const_iterator iter = svnInfo.begin(),
        end = svnInfo.end(); iter != end; ++iter) {
        const size_t pos = iter->find(':');
        if (pos != TString::npos) {
            const TString& left = Strip(iter->substr(0, pos));
            const TString& right = Strip(iter->substr(pos + 1));
            if (!left.empty() && !right.empty()) {
                infoMap.insert(std::make_pair(left, right));
            }
        }
    }

    TString programName = GetProgramName();
    programName = programName.substr(0, programName.find_first_of('.'));

    DEBUG_LOG << "Program name request result: " << programName << Endl;
    NMemInfo::TMemInfo mi = NMemInfo::GetMemInfo();
    NCpuLoad::TInfo cpu = NCpuLoad::Get();
    double la;
    NSystemInfo::LoadAverage(&la, 1);
    TServerInfo result;
    result("product", programName)
        ("Svn_root", infoMap["URL"])
        ("Svn_revision", infoMap["Last Changed Rev"])
        ("Svn_author", infoMap["Last Changed Author"])
        ("Build_date", infoMap["Build date"])
        ("Build_host", infoMap["Hostname"])
        ("sandbox_task_id", GetSandboxTaskId())
        ("timestamp", Seconds())
        ("mem_size_real", Sprintf("%0.3f", mi.RSS * 1.0 / (1024 * 1024)))
        ("mem_size_virtual", Sprintf("%0.3f", mi.VMS * 1.0 / (1024 * 1024)))
        ("cpu_load_user", cpu.User)
        ("cpu_load_system", cpu.System)
        ("cpu_count", NSystemInfo::CachedNumberOfCpus())
        ("load_average", la)
        ("total_mem_size", NSystemInfo::TotalMemorySize())
        ("status", GetProcStatus());
    if (TSlotsInfo::Instance.Dump.IsDefined())
        result("slot", TSlotsInfo::Instance.Dump);
    return result;
}

TServerInfo CollectServerInfo(TCollectServerInfo& collector) {
    SendGlobalMessage(collector);

    TServerInfo info = GetCommonServerInfo();
    collector.Fill(info);
    return info;
}

namespace NSaasServerInfo {
    TString GetInstanceTags(bool onlyWhiteList) {
        if (TSlotsInfo::Instance.Dump.IsDefined() && TSlotsInfo::Instance.Dump.Has("properties") && TSlotsInfo::Instance.Dump["properties"].Has("tags")) {
            const TString& allTagsStr = TSlotsInfo::Instance.Dump["properties"]["tags"].GetString();
            if (onlyWhiteList) {
                TVector<TString> result;
                StringSplitter(allTagsStr).Split(' ').Consume( [&](TStringBuf oneTag) {
                    if (oneTag.StartsWith("a_geo") || oneTag.StartsWith("a_prj")) { result.push_back(TString(oneTag)); }
                });
                return JoinStrings(result, ",");
            } else {
                return allTagsStr;
            }
        } else {
            return "";
        }
    }

    TString GetLabelVars(bool onlyWhiteList) {
        TVector<TString> labels;
        TSet<TString> whiteListedLabels({"LABELS_shard", "LABELS_saas_service", "LABELS_saas_ctype"});
        for (auto&& variable : NUtil::GetEnvironmentVariables()) {
            if (whiteListedLabels.contains(variable.first) || (variable.first.StartsWith("LABELS_") && !onlyWhiteList)) {
                labels.push_back(variable.first + "=" + variable.second);
            }
        }
        return JoinStrings(labels, ",");
    }

    TString GetNannyService() {
        if (TSlotsInfo::Instance.Dump.IsDefined() && TSlotsInfo::Instance.Dump.Has("properties") && TSlotsInfo::Instance.Dump["properties"].Has("NANNY_SERVICE_ID")) {
            return "nanny_" + TSlotsInfo::Instance.Dump["properties"]["NANNY_SERVICE_ID"].GetStringRobust();
        } else {
            return "";
        }
    }

    TString GetPseudoTagsString() {
        return GetInstanceTags(true) + "," + GetLabelVars(true) + "," + GetNannyService();
    }
}
