#include "script_same.h"
#include "filters.h"

#include <saas/deploy_manager/meta/cluster.h>
#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/deploy_manager/scripts/allocate_slots/common/intervals.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/scripts/common/slots_allocator/allocator.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 NRTYDeploy {

    bool TScriptAllocateSameSlots::Process(IDeployInfoRequest& request) {
        TClusterTask::TCgiContext context = TClusterTask::TCgiContext::Parse(request.GetRD().CgiParam);
        NDaemonController::TIntervalsByDC intervals;
        TString intervalsStr = request.GetRD().CgiParam.Get("intervals");
        TString defaultDC = request.GetRD().CgiParam.Get("default_dc");
        const TVector<TString> newSlotsPool = SplitString(request.GetRD().CgiParam.Get("new_slots_pool"), ",");
        NJson::TJsonValue intervalsJson;
        if (!ReadJsonFastTree(intervalsStr, &intervalsJson)) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "Incorrect intervals format " << intervalsStr;
            return false;
        }

        if (!intervals.DeserializeFromJson(intervalsJson)) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "Incorrect intervals info " << intervalsStr;
            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;

        if (!newSlotsPool.size()) {
            for (auto&& i : intervals.GetInfo()) {
                TString dc = i.first;
                if (dc == DC_NOT_DEFINED && !!defaultDC)
                    dc = defaultDC;
                NRTYCluster::TSlotsAllocator allocator(TVector<TString>(1, dc));
                allocator.PrefferedDCOnly = true;

                TFiltersAggregator filters(allocator, &request.GetCommonData().GetControllersChecker(), info, serviceInfo, i.second.size());

                NRTYCluster::TCTypeCluster::TPtr newSlots = cluster.FilterSlots(filters.GetGlobalFilters())->FilterSlots(filters.GetAllocFilters());
                if (newSlots->GetSlots().empty()) {
                    request.Output() << "HTTP/1.1 400 \r\n\r\n";
                    request.Output() << "We haven't enough slots";
                    return false;
                }
                ui32 index = 0;
                for (auto&& newSlot : newSlots->GetSlots()) {
                    NSearchMapParser::TSearchMapHost smh(newSlot.second.FullHost(), i.second[index], NSearchMapParser::Master, newSlot.second.Port, newSlot.second.IndexPort());
                    smh.DisableIndexing = true;
                    smh.DisableSearch = true;
                    poolResult.Add(smh);
                    ++index;
                }
            }
        } else {
            ui32 freeSlot = 0;
            for (auto&& i : intervals.GetInfo()) {
                for (auto&& s : i.second) {
                    bool found = false;
                    for (; (freeSlot < newSlotsPool.size()) && !found; ++freeSlot) {
                        NRTYCluster::TSlotData slotData;
                        TString slotName = newSlotsPool[freeSlot];
                        if (NRTYCluster::TSlotData::Parse(slotName, slotData)) {
                            NSearchMapParser::TSearchMapHost smh(slotData.FullHost(), s, NSearchMapParser::Master, slotData.Port, slotData.IndexPort());
                            smh.DisableIndexing = true;
                            smh.DisableSearch = true;
                            if (poolResult.Add(smh)) {
                                found = true;
                            }
                        }
                    }
                    if (!found) {
                        request.Output() << "HTTP/1.1 400 \r\n\r\n";
                        request.Output() << "We haven't enough slots";
                        return false;
                    }
                }
            }
        }

        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<TScriptAllocateSameSlots> TScriptAllocateSameSlots::Registrator("allocate_same_slots");
}
