#include "get_cluster_map.h"
#include "common/deploy/deploy_builder.h"
#include "cluster/cluster_task.h"

#include <saas/deploy_manager/scripts/common/scripts_helper.h>
#include <saas/deploy_manager/server/client/client.h>
#include <saas/library/searchmap/searchmap.h>
#include <saas/library/searchmap/parsers/json/json.h>
#include <saas/util/external/dc.h>
#include <library/cpp/json/json_writer.h>

using namespace NRTYDeploy;
using namespace NSaas;
using namespace NSearchMapParser;

class TClusterMapProcessor : public ISearchMapProcessor {
public:
    TClusterMapProcessor(NJson::TJsonValue& target)
        : Target(target)
        , UseDCUtil(TDatacenterUtil::Instance().IsInitialized())
    {}

    bool ProcessService(const TServiceSpecificOptions& options) override {
        NSearchMapParser::TJsonSearchMapParser::SerializeCommonService(Target, options);
        return true;
    }

    void Do(const TServiceSpecificOptions& /*sso*/, const TReplicaSpecificOptions& rso,
        const TInterval<TShardIndex>& /*interval*/, const TSearchInformation& host) override
    {
        const TString datacenter = UseDCUtil ? TDatacenterUtil::Instance().GetDatacenter(host.Name) : "unknown_dc";
        TSearchInformation patchedHost = host;
        patchedHost.DisableIndexing |= rso.DisableIndexing;
        patchedHost.DisableSearch |= rso.DisableSearch;
        NJson::TJsonValue& replica = Target["config_types"][rso.Alias];
        replica["update_frequency"] = rso.UpdateFrequency.ToString();
        NJson::TJsonValue& slot = replica["sources"][host.Shards.ToString()][datacenter][host.GetDescription()];
        slot.InsertValue("shards_min", host.Shards.GetMin());
        slot.InsertValue("shards_max", host.Shards.GetMax());
        slot.InsertValue("disable_indexing", host.DisableIndexing);
        slot.InsertValue("disable_search", host.DisableSearch);
        slot.InsertValue("disable_search_filtration", host.DisableSearchFiltration);
        slot.InsertValue("disable_fetch", host.DisableFetch);
    }
private:
    NJson::TJsonValue& Target;
    bool UseDCUtil;
};

class TSearchSourceBuilder {
public:
    TSearchSourceBuilder(const NSearchMapParser::TSearchMap& searchMap)
        : SearchMap(searchMap)
    {}

    NJson::TJsonValue GetClusterMap(const TString& serviceName = Default<TString>()) const {
        NJson::TJsonValue result;
        NJson::TJsonValue& cluster = result.InsertValue("cluster", NJson::JSON_MAP);
        const bool selectService = serviceName != Default<TString>();
        for (const auto& service : SearchMap.GetServiceMap()) {
            const TString& name = service.first;
            if (selectService && name != serviceName)
                continue;

            NJson::TJsonValue& serviceSection = cluster.InsertValue(name, NJson::JSON_MAP);
            TClusterMapProcessor processor(serviceSection);
            service.second.ProcessSearchMap(processor);
        }
        for (const auto& meta : SearchMap.GetMetaServices()) {
            if (selectService && meta.Name != serviceName)
                continue;
            cluster.InsertValue(meta.Name, meta.SerializeToJson());
        }
        return result;
    }

private:
    const NSearchMapParser::TSearchMap& SearchMap;
};

bool TGetClusterMapScript::Process(IDeployInfoRequest& request) {

    NRTYDeploy::TClusterTask::TCgiContext cgiContext = NRTYDeploy::TClusterTask::TCgiContext::Parse(request.GetRD().CgiParam);

    const TString& ctype = cgiContext.CType;
    const TString& service = cgiContext.Service;
    TString serviceType = cgiContext.ServiceType;

    NRTYDeployInfo::IDeployComponentInfo::TPtr info = NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(serviceType);
    info->SetInfo(&request, ctype);

    NSearchMapParser::TSearchMap ism = info->SearchMap(service);
    ism.Compile(false, false);

    TSearchSourceBuilder ssb(ism);
    {
        const NJson::TJsonValue& result = ssb.GetClusterMap(service);
        request.Output() << "HTTP/1.1 200 \r\n\r\n";
        request.Output() << NJson::WriteJson(&result, true, true);
    }
    return true;
};

IScript::TFactory::TRegistrator<TGetClusterMapScript> TGetClusterMapScript::Registrator("get_cluster_map");
