#include "task.h"
#include "action.h"

#include <saas/deploy_manager/meta/cluster.h>
#include <saas/deploy_manager/scripts/searchmap/action.h>
#include <saas/deploy_manager/scripts/deploy/action.h>
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <saas/deploy_manager/scripts/allocate_slots/common/intervals.h>
#include <saas/deploy_manager/scripts/allocate_slots/action/action_replica.h>

namespace NRTYDeploy {

    TAddIntSearchTask::TAddIntSearchTask(const NRTYCluster::TSlotsAllocator& slotsAllocator, const TClusterTask::TCgiContext& context, NRTYDeploy::ICommonData* commonData, ui32 intCount, ui32 replicasCount /*= 1*/)
        : TClusterTask(context, commonData, ADD_INTSEARCH_TASK_TYPE)
    {
        SlotsAllocator = slotsAllocator;
        ShardsCount = intCount;
        ReplicasCount = replicasCount;
        if (!intCount)
            ythrow yexception() << "incorrect intcount";
    }

    TString TAddIntSearchTask::GetReportConstruction() const {
        NJson::TJsonValue reply(NJson::JSON_MAP);
        reply.InsertValue("id_task", GetId());
        reply.InsertValue("new_slots", Pool.Serialize());
        return reply.GetStringRobust();
    }

    void TAddIntSearchTask::DoBuildTask() {

        NRTYDeployInfo::IDeployComponentInfo::TPtr info = NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(ServiceType);
        if (!info) {
            SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Incorrect service type: " + ServiceType);
            return;
        }
        info->SetInfo(CommonData, CType);

        const NSaas::TClusterConst* clusterPtr = NSaas::TClusterLocker::DefineConst(*CommonData, CType);
        if (!clusterPtr) {
            SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Can't read cluster for ctype " + CType);
            return;
        }

        const NSearchMapParser::TSearchMapService* serviceObj = clusterPtr->ServiceSearchMap(Service);
        if (!serviceObj) {
            SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Can't read service " + Service);
            return;
        }

        const NSearchMapParser::TSearchMapReplica* configTypeObj = serviceObj->GetReplica("default");
        if (!configTypeObj) {
            DEBUG_LOG << serviceObj->SerializeToProto().DebugString() << Endl;
            SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Can't read config type default");
            return;
        }

        TSet<NSearchMapParser::TShardsInterval> intervals;

        for (auto& i : configTypeObj->Hosts) {
            intervals.insert(i.Shards);
        }

        for (auto& i : intervals) {
            for (auto& j : intervals) {
                NSearchMapParser::TShardsInterval intersection;
                if (i != j && i.Intersection(j, intersection)) {
                    if (intersection.GetLength() != 0) {
                        SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Can't build int-s coverage. Shard intervals overlapped");
                        return;
                    }
                }
            }
        }

        TVector<NSearchMapParser::TShardsInterval> intVector(intervals.begin(), intervals.end());

        double countPerInt = 1.0 * (intervals.size()) / ShardsCount;

        if (countPerInt < 1) {
            SetStatus(ctsFailed, TClusterTask::rsFailedOnConstruction, "Can't build int-s coverage. Shard intervals count lesser then shards for int");
            return;
        }

        TVector<NSearchMapParser::TShardsInterval> shardsInt(ShardsCount);

        double intervalNum = 0;
        for (ui32 i = 0; i < ShardsCount; ++i) {
            ui32 intervalFrom = floor(intervalNum);
            ui32 intervalTo = floor(intervalNum + countPerInt);
            shardsInt[i] = intVector[intervalFrom];
            shardsInt[i].SetMax(intVector[intervalTo - 1].GetMax());
            intervalNum += countPerInt;
        }

        NDaemonController::TIntervalsByDC intervalsByDC;
        for (auto&& i : shardsInt) {
            intervalsByDC.AddInfo("*", i);
        }

        NDaemonController::TAction::TPtr actionModifySearchMap = new NDaemonController::TAllocateSlotsReplicasAction(intervalsByDC, ReplicasCount, SlotsAllocator, Service, CType, INT_SERVICE);
        GetScript()->AddAction(CommonData->GetDeployManagerBalanserHost(), CommonData->GetDeployManagerBalanserPort(), CommonData->GetDeployManagerBalanserUriPrefix(), actionModifySearchMap);

        NDaemonController::TAction::TPtr deploy = new NRTYDeploy::TDeployAction(Service, CType, "NEW",
            NDaemonController::apStartAndWait, INT_SERVICE, 0.01f, nullptr, TString());
        GetScript()->AddAction(CommonData->GetDeployManagerBalanserHost(), CommonData->GetDeployManagerBalanserPort(), CommonData->GetDeployManagerBalanserUriPrefix(), deploy);

        NDaemonController::TAction::TPtr deploySP = new NRTYDeploy::TDeployAction(SEARCH_PROXY_SERVICE, CType, "CURRENT",
            NDaemonController::apStartAndWait, SEARCH_PROXY_SERVICE, 0.01, nullptr, "searchmap.json");
        GetScript()->AddAction(CommonData->GetDeployManagerBalanserHost(), CommonData->GetDeployManagerBalanserPort(), CommonData->GetDeployManagerBalanserUriPrefix(), deploySP);
    }
};
