#include "get_slot_info.h"

#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <saas/deploy_manager/scripts/common/global_names/global_names.h>
#include <saas/library/searchmap/searchmap.h>

namespace NRTYDeploy {
    namespace {
        class TSlotFinder: public NSearchMapParser::ISearchMapScannerCallback {
        public:
            TSlotFinder(TResourcesManager::TSlotsInfo& result,
                const TString& hostName, const TString& slot, const TString& ctype, const TString& serviceType)
                : Result(result)
                , HostName(hostName)
                , CType(ctype)
                , ServiceType(serviceType)
            {
                if (slot) {
                    NRTYCluster::TSlotData sd;
                    if (!sd.Parse(slot, sd))
                        ythrow yexception() << "invalid slot name " << slot;
                    Slot = sd.ShortSlotName();
                }
            }

            void OnHost(const NSearchMapParser::TSearchMapHost& host, const NSearchMapParser::TSearchMapReplica& replica, const NSearchMapParser::TSearchMapService& service) override {
                if (!!Slot && host.GetShortSlotName() == Slot || !!HostName && host.Name == HostName || (!HostName && !Slot))
                    Result.push_back(host.GetSlotInfo(replica.Alias, service.Name, ServiceType, CType));
            }

        private:
            TResourcesManager::TSlotsInfo& Result;
            TString HostName;
            TString Slot;
            TString CType;
            TString ServiceType;
        };

        const TSet<TString> SourceTransferCtypes = {"stable", "stable_kv", "stable_middle_kv", "prestable"};
    }

    bool GetSlotInfo(const TString& host, const TString& slot, TResourcesManager::TSlotsInfo& result, const NSaas::TSlotInfo& context, IDeployInfoRequest& request) {
        TVector<TString> ctypes;
        if (context.CType) {
            ctypes.reserve(1);
            ctypes.push_back(context.CType);
        }
        else
            ctypes = request.GetResourcesManager().GetCTypes();

        TSet<TString> keys;
        if (context.ServiceType) {
            keys.insert(context.ServiceType);
        } else {
            Singleton<NRTYDeployInfo::IDeployComponentInfo::TFactory>()->GetKeys(keys);
        }

        for (auto& ctype : ctypes) {
            for (auto& comp : keys) {
                NRTYDeployInfo::IDeployComponentInfo::TPtr info = NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(comp);
                if (!!info) {
                    info->SetInfo(&request.GetCommonData(), ctype);
                    TSlotFinder finder(result, host, slot, ctype, comp);
                    info->SearchMap().Scan(finder);
                    NSearchMapParser::TSearchMap ism = info->SearchMap(UNUSED_SERVICE);
                    ism.Scan(finder);
                } else {
                    return false;
                }
            }
        }
        return true;
    }

NSaas::TSlotInfo GetSlotInfo(const TString& slot, const NSaas::TSlotInfo& context, IDeployInfoRequest& request) {
        TResourcesManager::TSlotsInfo slotsInfo;
        if (!GetSlotInfo("", slot, slotsInfo, context, request))
            ythrow yexception() << "Cannot find slot " << slot << Endl;
        if (slotsInfo.empty()) {
            NSaas::TSlotInfo result;
            result.CType = context.CType ? context.CType : UNUSED_CTYPE;
            result.Service = context.Service ? context.Service : UNUSED_SERVICE;
            result.ServiceType = context.ServiceType ? context.ServiceType : UNUSED_SERVICE_TYPE;
            result.Slot = slot;
            result.DisableIndexing = true;
            result.DisableSearch = true;
            result.ShardMin = 0;
            result.ShardMax = 0;
            return result;
        }
        if (slotsInfo.size() != 1) {
            TVector<size_t> specificBundleIndices;
            for (size_t i = 0; i < slotsInfo.size(); ++i) {
                if (!SourceTransferCtypes.contains(slotsInfo[i].CType))
                    specificBundleIndices.push_back(i);
            }
            if (specificBundleIndices.size() == 1) {
                return slotsInfo[specificBundleIndices[0]];
            }
            TStringStream e;
            e << slotsInfo.size() << " places of slot " << slot << ":" << Endl;
            for (size_t i = 0; i < slotsInfo.size(); ++i)
                e << slotsInfo[i].ToString() << Endl;
            ythrow yexception() << e.Str();
        }
        return slotsInfo[0];
    }
};
