#include "get_services.h"
#include "cluster/cluster_task.h"
#include "common/deploy/deploy_builder.h"
#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/deploy_manager/server/client/client.h>
#include <library/cpp/json/json_writer.h>

#include <util/string/type.h>

namespace NRTYDeploy {
    bool TScriptGetServices::Process(IDeployInfoRequest& request) {
        THashSet<TString> usedServices;
        NJson::TJsonValue json(NJson::JSON_MAP);
        const NRTYDeploy::TClusterTask::TCgiContext cgiContext = NRTYDeploy::TClusterTask::TCgiContext::Parse(request.GetRD().CgiParam);
        const bool fillSlots = IsTrue(request.GetRD().CgiParam.Get("fill_slots"));

        TSet<TString> comps;
        if (request.GetRD().CgiParam.Has("service_type"))
            comps.insert(cgiContext.ServiceType);
        else {
            Singleton<NRTYDeployInfo::IDeployComponentInfo::TFactory>()->GetKeys(comps);
        }
        const TVector<TString>& nodes = request.GetResourcesManager().GetCTypes();
        for (ui32 i = 0; i < nodes.size(); ++i) {
            NJson::TJsonValue& services = json.InsertValue(nodes[i], NJson::JSON_MAP);
            for (const auto& comp : comps) {
                NJson::TJsonValue& servType = services.InsertValue(comp, NJson::JSON_MAP);
                NRTYDeployInfo::IDeployComponentInfo::TPtr info = NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(comp);
                info->SetInfo(&request, nodes[i]);
                const NSearchMapParser::TSearchMap* ism;
                try {
                    ism = &info->SearchMap();
                } catch (...) {
                    continue;
                }
                try {
                    for (auto& service: ism->GetInternalSearchMap()) {
                        NJson::TJsonValue& slotsArray = servType.InsertValue(service.Name, NJson::JSON_ARRAY);
                        if (fillSlots) {
                            for (const auto& replica: service.Replicas) {
                                for (const auto& host: replica.Hosts) {
                                    slotsArray.AppendValue(TString::Join(host.Name, ':', ToString(host.SearchPort)));
                                }
                            }
                        }
                        usedServices.insert(service.Name);
                    }
                    for (auto& metaService: ism->GetMetaServices()) {
                        servType.InsertValue(metaService.Name, NJson::JSON_ARRAY);
                        usedServices.insert(metaService.Name);
                    }
                } catch (yexception& e) {
                    request.Output() << "HTTP/1.1 500 \r\n\r\n";
                    request.Output() << "searchmap parsing failed: " << e.what();
                    return false;
                }
            }
        }
        NJson::TJsonValue& services = json.InsertValue(UNUSED_SERVICE, NJson::JSON_ARRAY);
        if (fillSlots) {
            TVector<TString> unusedNodes;
            if (request.GetStorage().GetNodes("configs", unusedNodes, true)) {
                for (TVector<TString>::const_iterator name = unusedNodes.begin(); name != unusedNodes.end(); ++name)
                    if (usedServices.find(*name) == usedServices.end())
                        services.AppendValue(*name);
            }
        }
        TStringStream ss;
        NJson::WriteJson(&ss, &json, true, true, false);
        request.Output() << "HTTP/1.1 200 \r\n\r\n";
        request.Output() << ss.Str();
        return true;
    }

    IScript::TFactory::TRegistrator<TScriptGetServices> TScriptGetServices::Registrator("get_services");
}
