#include "deploy_real_service_builder.h"
#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/library/searchmap/parsers/json/json.h>

namespace NRTYDeployInfo {


    void TRealServiceDeployInfo::AddNewSlots(const NSearchMapParser::TSlotsPool& slotsPool, NSaas::TCluster& cluster) {
        NSearchMapParser::TSearchMapService* serviceObj = ServiceSearchMap(cluster);
        if (!serviceObj) {
            ythrow yexception() << "Incorrect service name " << Service;
        }
        NSearchMapParser::TSearchMapReplica* configTypePtr = serviceObj->GetReplica("default");
        if (!configTypePtr) {
            configTypePtr = serviceObj->AddReplica("default");
        }

        bool notFirstReplica = DisableNotFirstReplics() && configTypePtr->GetSlots().size();

        if (slotsPool.Hosts.empty() && slotsPool.EndPointSets.empty()) {
            ythrow yexception() << "add must be called with non-empty slots pool";
        }
        auto slotsVector = slotsPool.GetSlots();
        for (auto& smh : slotsVector) {
            smh.DisableIndexing = notFirstReplica;
            smh.DisableSearch = notFirstReplica;
            if (!NSlotNameUtil::IsNormalDC(smh.DC)) {
                smh.DC = TDatacenterUtil::Instance().GetDatacenter(smh.Name);
                if (!NSlotNameUtil::IsNormalDC(smh.DC))
                    smh.DC = "";
            }
            INFO_LOG << "Add host : " << smh.Serialize().GetStringRobust() << Endl;
            if (!configTypePtr->Add(smh)) {
                ythrow yexception() << "Can't add slot " << smh.GetSlotName();
            }
        }
        slotsVector = slotsPool.GetEndPointSets();
        for (auto& smh : slotsVector) {
            INFO_LOG << "Add endpoints : " << smh.Serialize().GetStringRobust() << Endl;
            if (!configTypePtr->Add(smh)) {
                ythrow yexception() << "Can't add endpoints " << smh.GetSlotName();
            }
        }
    }

    void TRealServiceDeployInfo::DoCorrectTopologyAction(const TCgiParameters& cgi, const NSearchMapParser::TSlotsPool* slotsPool, NSaas::TCluster& cluster, const TString& action) {
        NSearchMapParser::TSearchMapService* serviceObj = ServiceSearchMap(cluster);
        if (!serviceObj) {
            ythrow yexception() << "Incorrect service name " << Service;
        }
        NSearchMapParser::TSearchMapReplica* configTypePtr = serviceObj->GetReplica("default");
        if (!configTypePtr) {
            if (action == "add") {
                configTypePtr = serviceObj->AddReplica("default");
            }
            else {
                ythrow yexception() << "Incorrect config type name default";
            }
        }

        auto& mutableSlots = configTypePtr->GetMutableSlots();
        auto& mutableEndpointSets = configTypePtr->GetMutableEndPointSets();

        if (action == "add") {
            if (!slotsPool) {
                ythrow yexception() << "add must be called with full slots pool";
            }
            AddNewSlots(*slotsPool, cluster);
        }
        else if (action == "release") {
            NSearchMapParser::TSlotsPool slots;
            if (slotsPool)
                slots = *slotsPool;
            if (!slots.Hosts.size() && !slots.EndPointSets.size()) {
                const TString slotsVector = cgi.Get("slots_vector");
                if (!slotsVector)
                    ythrow yexception() << "slots or slots_vector must be set";
                slots.Parse(SplitString(slotsVector, ","));
            }

            for (ui32 i = 0; i < mutableSlots.size();) {
                if (slots.SlotExists(mutableSlots[i].GetSlotName())) {
                    mutableSlots.erase(mutableSlots.begin() + i);
                }
                else {
                    ++i;
                }
            }

            for (ui32 i = 0; i < mutableEndpointSets.size();) {
                if (slots.EndpointSetExists(mutableEndpointSets[i].GetSlotName())) {
                    mutableEndpointSets.erase(mutableEndpointSets.begin() + i);
                }
                else {
                    ++i;
                }
            }
        }
        else {
            using TShardsInterval = NSearchMapParser::TShardsInterval;
            TMap<TShardsInterval, TShardsInterval> intervalsReplaceMap;
            if (action == "replace_intervals") {
                for (ui32 i = 0; i < cgi.NumOfValues("interval"); ++i) {
                    TVector<TString> intervals = SplitString(cgi.Get("interval", i), ";");
                    for (const TString& intervalStr : intervals) {
                        TVector<TString> fromTo = SplitString(intervalStr, "->");
                        if (fromTo.size() != 2)
                            ythrow yexception() << "incorrect interval " << intervalStr;
                        TShardsInterval from, to;
                        if (!TShardsInterval::Parse(fromTo[0], from) || !TShardsInterval::Parse(fromTo[1], to))
                            ythrow yexception() << "incorrect interval " << intervalStr;
                        intervalsReplaceMap[from] = to;
                    }
                }
                if (intervalsReplaceMap.empty())
                    return;
            }
            TMap<TString, TString> slotToContainer;
            if (action == "switch_container") {
                NRTYCluster::TCluster freeCluster;
                if (!GetCommon()->GetResourcesManager().GetFreeSlotsForService(GetCType(),
                    ComponentInfo->GetComponentName(), Service, ComponentInfo->SearchMap("*"), freeCluster)) {
                    return;
                }
                for (const auto& s : freeCluster.GetSlots()) {
                    if (s.second.GetRealHost() != s.second.FullHost()) {
                        const TString sn = s.second.GetRealHost() + ":" + ToString(s.second.Port);
                        if (slotsPool->SlotExists(sn)) {
                            slotToContainer[sn] = s.second.FullHost();
                        }
                    }
                }
            }

            for (auto& i : mutableSlots) {
                if (action == "replace_intervals") {
                    auto iter = intervalsReplaceMap.find(i.Shards);
                    if (iter != intervalsReplaceMap.end())
                        i.Shards = iter->second;
                }

                if (slotsPool && !slotsPool->SlotExists(i.GetSlotName()))
                    continue;

                if (action == "switch_container") {
                    if (!!slotToContainer[i.GetSlotName()]) {
                        i.Name = slotToContainer[i.GetSlotName()];
                    }
                }
                else if (action == "disable_search") {
                    i.DisableSearch = true;
                }
                else if (action == "enable_search") {
                    i.DisableSearch = false;
                }
                else if (action == "disable_indexing") {
                    i.DisableIndexing = true;
                }
                else if (action == "enable_indexing") {
                    i.DisableIndexing = false;
                }
                else if (action == "disable_search_filtration") {
                    i.DisableSearchFiltration = true;
                }
                else if (action == "enable_search_filtration") {
                    i.DisableSearchFiltration = false;
                }
                else if (action == "disable_fetch") {
                    i.DisableFetch = true;
                }
                else if (action == "enable_fetch") {
                    i.DisableFetch = false;
                }
                else {
                    ythrow yexception() << "Incorrect action " << action;
                }
            }
        }
    }

    bool TRealServiceDeployInfo::DoBuildFilesInfo(const NRTYDeploy::TFileContentGenerator::TContext& context, TStringStream& errors, bool forcedOnly) {
        if (!TServiceDeployInfo::DoBuildFilesInfo(context, errors, forcedOnly))
            return false;

        if (!NRTYDeploy::TFileContentGenerator::AddGenerators(Nodes, *GetCommon(), "/common", "", context.SlotInfo)) {
            ERROR_LOG << "Incorrect resource " << "common" << Endl;
            errors << "Incorrect resource " << "common" << Endl;
            return false;
        }
        return true;
    }

    void TRealServiceDeployInfo::AddService(NSaas::TCluster& cluster) {
        AddService(cluster, Service);
    }

    void TRealServiceDeployInfo::EditService(NSaas::TCluster& cluster, const NJson::TJsonValue& serviceJson) {
        NSearchMapParser::TSearchMapService* service = ServiceSearchMap(cluster);
        if (!service) {
            ythrow yexception() << "Incorrect service name: " << Service;
        }
        NSearchMapParser::TSearchMapCommonServiceParser(Service, serviceJson, NSearchMapParser::DefaultParsingSettings).FillCommonService(*service);
    }

    void TRealServiceDeployInfo::CopyService(NSaas::TCluster& cluster, const TString& newName, const bool clearReplicas) {
        if(cluster.IsRTYServiceExist(newName)) {
            ythrow yexception() << "RTY service with name " << newName << " already exists";
        }
        NSearchMapParser::TSearchMapService* service = ServiceSearchMap(cluster);
        if (!service) {
            ythrow yexception() << "Incorrect service name: " << Service;
        }
        NSearchMapParser::TSearchMapService newService = *service;
        newService.Name = newName;
        if (clearReplicas) {
            newService.Replicas.clear();
        }

        cluster.AddRTYService(newService);
    }
}
