#include "script.h"

#include <saas/deploy_manager/meta/cluster.h>
#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/deploy_manager/modules/service_discovery/sd_module.h>
#include <saas/deploy_manager/scripts/cluster/cluster_task.h>
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <saas/deploy_manager/scripts/common/scripts_helper.h>
#include <saas/deploy_manager/server/client/client.h>
#include <saas/util/external/dc.h>
#include <saas/util/external/diff.h>
#include <library/cpp/json/json_reader.h>
#include <util/string/type.h>

namespace {

    void DeserializeEndpoints(const TString& epsStr, TVector<NSearchMapParser::TSearchMapHost>& newEpsAsSearchMapHosts) {
        TVector<TString> endpoints = SplitString(epsStr, ",");

        //cluster@eps.name:shardmin-shardmax
        for (size_t i=0; i<endpoints.size(); ++i) {
            TVector<TString> parts = StringSplitter(endpoints[i]).SplitBySet("@:").SkipEmpty();
            if (parts.size() != 3)
                ythrow yexception() << "wrong endpoint format (must be cluster@endpointset:shards): " << endpoints[i];
            if (!NSlotNameUtil::Cluster2DC.contains(parts[0]))
                ythrow yexception() << "unknown cluster: " << parts[0] << " in " << endpoints[i];
            const TString dc = NSlotNameUtil::DCFromEndpointSet(endpoints[i]);
            TVector<TString> shards = SplitString(parts[2], "-");
            if (shards.size() != 2)
                ythrow yexception() << "shards must be uintegers in shardMin-shardMax format, got >2parts: " << parts[2] << " in " << endpoints[i];
            NSearchMapParser::TShardIndex shardMin, shardMax;
            if (!TryFromString(shards[0], shardMin) || !TryFromString(shards[1], shardMax))
                ythrow yexception() << "shards must be uintegers in shardMin-shardMax format, got " << shards[0] << ", " << shards[1] << " in " << endpoints[i];

            TInterval<NSearchMapParser::TShardIndex> shardsInt(shardMin, shardMax);
            NRTYCluster::TSlotData slotData(parts[0] + '@' + parts[1], 80);
            slotData.SetDC(dc);
            NSearchMapParser::TSearchMapHost smh(slotData.FullHost(), shardsInt, NSearchMapParser::Master, slotData.Port, slotData.IndexPort());
            smh.IsSd = true;
            smh.DC = dc;
            newEpsAsSearchMapHosts.push_back(smh);
        }
    }
}

namespace NRTYDeploy {

    bool TScriptAddEndpointSets::Process(IDeployInfoRequest& request) {
        TClusterTask::TCgiContext context = TClusterTask::TCgiContext::Parse(request.GetRD().CgiParam);
        TString epsStr = request.GetRD().CgiParam.Get("eps");
        // now: &eps=dc@eps1:0-123456
        // probably, future format: &eps={"dc@eps1":"0-123456", ...}
        TVector<NSearchMapParser::TSearchMapHost> newEndpointSets;
        try {
            DeserializeEndpoints(epsStr, newEndpointSets);
        } catch (yexception&) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "Cannot parse endpointsets: " << CurrentExceptionMessage();
            return false;
        }
        if (newEndpointSets.size() == 0) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "No endpointsets found in &eps= " ;
            return false;
        }
        for (auto&& smh : newEndpointSets) {
            if (!request.GetSDModule().AddEndpointSet(smh.Name)) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "Probably incorrect endpointset: " << smh.Name;
                return false;
            }
        }

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

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

        NSaas::TClusterLocker clusterPtr = NSaas::TClusterLocker::DefineMutable(request, ctype);
        if (!clusterPtr) {
            request.Output() << "HTTP/1.1 500 \r\n\r\n";
            request.Output() << "Resource " << "/common/" << ctype << "/cluster.meta is not available for modification";
            return false;
        }
        SetData(clusterPtr.GetContent(), "");

        NRTYCluster::TCTypeCluster cluster;

        auto&& clusterInfo = request.GetCommonData().GetResourcesManager().GetCluster(serviceType, service);

        if (!clusterInfo.GetCTypeCluster(ctype, cluster)) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "Incorrect ctype: " << ctype;
            return false;
        }

        NSearchMapParser::TSlotsPool poolResult;

        for (auto&& smh : newEndpointSets) {
            poolResult.Add(smh);
        }

        serviceInfo->AddNewSlots(poolResult, *clusterPtr);

        TString errorMessage;
        if (!clusterPtr->Validate(errorMessage)) {
            DEBUG_LOG << "Can't store cluster meta: " << errorMessage << Endl;
            request.Output() << "HTTP/1.1 500 \r\n\r\n";
            request.Output() << "Incorrect new cluster: " << errorMessage;
            return false;
        }
        SetData("", clusterPtr.GetContent());
        if (!clusterPtr.Save()) {
            request.Output() << "HTTP/1.1 500 \r\n\r\n";
            request.Output() << "Incorrect storage structure";
            return false;
        }
        request.Output() << "HTTP/1.1 200 \r\n\r\n";
        request.Output() << poolResult.Serialize().GetStringRobust();

        return true;
    };

    IScript::TFactory::TRegistrator<TScriptAddEndpointSets> TScriptAddEndpointSets::Registrator("add_endpointsets");
}
