#include "script.h"
#include <saas/deploy_manager/server/client/client.h>
#include <library/cpp/json/json_writer.h>
#include <library/cpp/digest/md5/md5.h>
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <saas/util/logging/trace.h>

namespace NRTYDeploy {

    TScriptCheckConfigs::TScriptCheckConfigs() : TScriptBroadcast() {
        NeedStatus = false;
    }

    bool TScriptCheckConfigs::Process(IDeployInfoRequest& request) {
        TVector<TString> hashes = SplitString(request.GetRD().CgiParam.Get("interest"), ",");
        if (request.GetRD().CgiParam.Has("full_data_limit"))
            FullDataLimit = FromString<ui32>(request.GetRD().CgiParam.Get("full_data_limit"));
        if (!hashes) {
            InterestHashes.insert(hnNEW);
            InterestHashes.insert(hnCURRENT);
        } else {
            for (const auto& h : hashes) {
                THashName hn = FromString<THashName>(h);
                if (hn != hnTAKED)
                    InterestHashes.insert(hn);
            }
        }

        return TScriptBroadcast::Process(request);
    }

    void TScriptCheckConfigs::ProcessConfigs(TServiceDescritption& service, const TFileContentGenerator::TContext& context, const TString& slot, THashName hashName) {
        TString filter;
        for (const auto& file : service.Configs[hashName]) {
            TString name = file->GetRename(context);
            if (!name)
                continue;
            if (!!filter)
                filter += "|";
            filter += name;
            TString contentHash;
            TString content;
            try {
                if (!file->GetContentHash(contentHash, *CommonData, context))
                    continue;
                if (FullDataLimit) {
                    if (!file->GetContent(content, *CommonData, context)) {
                         ythrow yexception() << "cannot get content";
                    }
                    content = content.substr(0, FullDataLimit);
                }
            } catch (...) {
                ERROR_LOG << "Can't restore content for " << name << Endl;
                continue;
            }
            TFileHashes& hashes = service.Hashes[slot][name];
            hashes.Set(hashName, contentHash);
            if (FullDataLimit)
                hashes.Set((THashName)(hashName + 10000), content);
        }
        Command = "command=get_configs_hashes&filter=" + filter;
    }

    void TScriptCheckConfigs::OnHost(const NSearchMapParser::TSearchMapHost& host, const NSearchMapParser::TSearchMapReplica& replica, const NSearchMapParser::TSearchMapService& service) {
        TFileContentGenerator::TContext context;
        context.NoAnyPatch = false;
        context.SlotInfo = host.GetSlotInfo(replica.Alias, service.Name, ServiceType, CType);
        if (!NSlotNameUtil::IsNormalDC(context.SlotInfo.DC)) {
            context.SlotInfo.DC = TDatacenterUtil::Instance().GetDatacenter(host.Name);
        }

        ProcessAllConfigs(context);
        TScriptBroadcast::OnHost(host, replica, service);
    }

    bool TScriptCheckConfigs::MakeReport(IOutputStream& os) {
        NUtil::TTracer timer("TScriptCheckConfigs::MakeReport");
        NJson::TJsonValue result;
        for (NJson::TJsonValue::TMapType::const_iterator service = Report.GetMap().begin(); service != Report.GetMap().end(); ++service) {
            NJson::TJsonValue& serviceRes = result[service->first];
            for (NJson::TJsonValue::TMapType::const_iterator slot = service->second.GetMap().begin(); slot != service->second.GetMap().end(); ++slot) {
                const NJson::TJsonValue& taked = slot->second["result"];
                NJson::TJsonValue& slotRes = serviceRes[slot->first];
                for (NJson::TJsonValue::TMapType::const_iterator file = taked.GetMap().begin(); file != taked.GetMap().end(); ++file)
                    slotRes[file->first].InsertValue(ToString(hnTAKED), file->second.GetStringRobust());
            }
        }

        for (TServices::const_iterator service = Services.begin(); service != Services.end(); ++service) {
            NJson::TJsonValue& serviceRes = result[service->first];
            for (THashesBySlot::const_iterator slot = service->second->Hashes.begin(); slot != service->second->Hashes.end(); ++slot) {
                NJson::TJsonValue& slotRes = serviceRes[slot->first];
                for (THashesByFile::const_iterator file = slot->second.begin(); file != slot->second.end(); ++file) {
                    NJson::TJsonValue& res = slotRes[file->first];
                    for (THashName hn : InterestHashes) {
                        res.InsertValue(ToString(hn), file->second.Get(hn));
                        if (FullDataLimit) {
                            THashName fullHN = (THashName)(10000 + hn);
                            res.InsertValue(ToString(fullHN), file->second.Get(fullHN));
                        }
                    }
                }
            }
        }
        if (Report.Has("content_hash"))
            result.InsertValue("content_hash", Report["content_hash"]);
        NJson::WriteJson(&os, &result, true, true);
        return true;
    }

    void TScriptCheckConfigs::ProcessAllConfigs(TFileContentGenerator::TContext &context)
    {
        TServiceDescritption::TPtr& serviceDescr = Services[context.SlotInfo.Service];

        if (!serviceDescr) {
            serviceDescr.Reset(new TServiceDescritption);
            TStringStream errors;
            NRTYDeployInfo::IDeployComponentInfo::TPtr component(NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(ServiceType));
            component->SetInfo(CommonData, context.SlotInfo.CType);
            NRTYDeployInfo::IDeployServiceInfo::TPtr builder = component->BuildServiceInfo(context.SlotInfo.Service);

            if (InterestHashes.contains(hnNEW)) {
                if (!builder->BuildFilesInfo(context, errors, "NEW"))
                    ythrow yexception() << "cannot get new configs for " << context.SlotInfo.Slot << ": " << errors.Str();
                serviceDescr->Configs[hnNEW] = builder->GetNodes();
            }
            if (InterestHashes.contains(hnCURRENT)) {
                if (!builder->BuildFilesInfo(context, errors, "CURRENT"))
                    ythrow yexception() << "cannot get current configs for " << context.SlotInfo.Slot << ": " << errors.Str();
                serviceDescr->Configs[hnCURRENT] = builder->GetNodes();
            }
        }
        for (THashName hn : InterestHashes)
            ProcessConfigs(*serviceDescr, context, context.SlotInfo.Slot, hn);
    }

    IScript::TFactory::TRegistrator<TScriptCheckConfigs> TScriptCheckConfigs::Registrator("check_configs");
}

template <>
void Out<NRTYDeploy::TScriptCheckConfigs::THashName>(IOutputStream& output, TTypeTraits<NRTYDeploy::TScriptCheckConfigs::THashName>::TFuncParam hn) {
    switch (hn) {
    case NRTYDeploy::TScriptCheckConfigs::hnNEW:
        output << "last_stored";
        break;
    case NRTYDeploy::TScriptCheckConfigs::hnNEW_FULL:
        output << "last_stored_full";
        break;
    case NRTYDeploy::TScriptCheckConfigs::hnCURRENT:
        output << "last_deployed";
        break;
    case NRTYDeploy::TScriptCheckConfigs::hnCURRENT_FULL:
        output << "last_deployed_full";
        break;
    case NRTYDeploy::TScriptCheckConfigs::hnTAKED:
        output << "from_host";
        break;
    default:
        FAIL_LOG("invalid HashName %i", int(hn));
        break;
    }
}

template <>
NRTYDeploy::TScriptCheckConfigs::THashName FromStringImpl<NRTYDeploy::TScriptCheckConfigs::THashName>(const char* data, size_t length) {
    if (strcmp(data, "last_stored") == 0)
        return  NRTYDeploy::TScriptCheckConfigs::hnNEW;
    if (strcmp(data, "last_stored_full") == 0)
        return  NRTYDeploy::TScriptCheckConfigs::hnNEW_FULL;
    if (strcmp(data, "last_deployed") == 0)
        return  NRTYDeploy::TScriptCheckConfigs::hnCURRENT;
    if (strcmp(data, "last_deployed_full") == 0)
        return  NRTYDeploy::TScriptCheckConfigs::hnCURRENT_FULL;
    if (strcmp(data, "from_host") == 0)
        return  NRTYDeploy::TScriptCheckConfigs::hnTAKED;
    throw yexception() << "cannot parse HashName from string " << TString(data, length);
}

