#pragma once

#include <saas/deploy_manager/scripts/common/scripts_helper.h>
#include <saas/deploy_manager/scripts/common/deploy/abstract/deploy_factory.h>
#include <saas/deploy_manager/storage/abstract.h>
#include <library/cpp/object_factory/object_factory.h>
#include <saas/deploy_manager/meta/cluster.h>
#include <saas/library/searchmap/searchmap.h>
#include <saas/util/cluster/cluster.h>


namespace NRTYDeployInfo {

#define CURRENT_CONFIGS_FILE "current_configs"

    class IDeployComponentInfo;

    class IDeployServiceInfo : public NSearchMapParser::IUsedSlotsPool {
    protected:
        NRTYDeploy::TNodesStructure Nodes;
        NRTYDeploy::TNodesStructure NodesCurrent;
        TString Service;
        IDeployComponentInfo* ComponentInfo;
    public:
        typedef TAtomicSharedPtr<IDeployServiceInfo> TPtr;
        typedef NObjectFactory::TObjectFactory<IDeployServiceInfo, TString> TFactory;
    public:
        virtual ~IDeployServiceInfo() {}

        virtual TDuration GetStopTimeout() const = 0;

        const NRTYDeploy::TNodesStructure& GetNodes() const {
            return Nodes;
        }

        virtual TVector<NSearchMapParser::TSearchMapHost> GetUsedSlots() const override;

        const NRTYDeploy::ICommonData* GetCommon() const;

        TString GetCType() const;

        const NRTYDeploy::TNodesStructure& GetNodesCurrent() const {
            return NodesCurrent;
        }

        virtual TString GetConfigsPath() const {
            return "/configs/" + Service;
        }

        virtual TString GetCurrentConfigsPath() const;

        virtual bool DoBuildFilesInfo(const NRTYDeploy::TFileContentGenerator::TContext& context, TStringStream& errors, bool forcedOnly);

        void SetInfo(IDeployComponentInfo* componentInfo, const TString& service);

        bool DoBuildNodesCurrent(const TString& currConf, NRTYDeploy::TNodesStructure& nodes);

        bool BuildNodesCurrent(TStringStream& errors, NRTYDeploy::TNodesStructure* nodesExt, i64& version);

        bool BuildFilesInfo(const NRTYDeploy::TFileContentGenerator::TContext& context, TStringStream& errors, const TString& version);

        virtual void CorrectTopology(const TCgiParameters& /*cgi*/, const NSearchMapParser::TSlotsPool* /*slotsPool*/, NSaas::TCluster& /*cluster*/) {
            ythrow yexception() << "CorrectTopology Not implemented";
        }

        virtual void AddNewSlots(const NSearchMapParser::TSlotsPool& /*slotsPool*/, NSaas::TCluster& /*cluster*/) {
            ythrow yexception() << "AddNewSlots Not implemented";
        }
    };

    class IDeployComponentInfo : public IDeployComponentInfoAbstract {
    protected:
        TString CType;
        mutable const NRTYDeploy::ICommonData* Common;

    protected:
        static void BuildFakeSearchMap(const NRTYCluster::TCTypeCluster& cluster, const TString& serviceName, NSearchMapParser::TSearchMap& result);
        NSearchMapParser::TSearchMap SearchMap(const TString& service, bool unusedOnly) const;

    public:
        typedef TAtomicSharedPtr<IDeployComponentInfo> TPtr;
        typedef NObjectFactory::TObjectFactory<IDeployComponentInfo, TString> TFactory;
        virtual ~IDeployComponentInfo() {}

        virtual TString GetComponentName() const = 0;

        void SetInfo(const NRTYDeploy::ICommonData* common, const TString& cType) {
            CType = cType;
            Common = common;
        }

        TString GetCType() const {
            return CType;
        }

        const NRTYDeploy::ICommonData* GetCommon() const {
            if (!Common)
                ythrow yexception() << "CommonData not initialized";
            return Common;
        }

        virtual NSearchMapParser::TSearchMap DoSearchMapBySlot(const TString& /*slotName*/, const NSaas::TClusterConst* /*locker*/, const TString& /*dc*/) const {
            FAIL_LOG("NOT APPLICABLE");
        }

        struct TFunctorSlotsCompare {
            TString PrioritizedDC;

            TFunctorSlotsCompare(const TFunctorSlotsCompare& fsc) {
                PrioritizedDC = fsc.PrioritizedDC;
            }

            TFunctorSlotsCompare(const TString& slotName) {
                NRTYCluster::TSlotData slotData;
                VERIFY_WITH_LOG(NRTYCluster::TSlotData::Parse(slotName, slotData), "Incorrect slot name %s", slotName.data());
                PrioritizedDC = slotData.GetDC();
            }

            bool operator()(const NSearchMapParser::TSearchMapHost& leftHost, const NSearchMapParser::TSearchMapHost& rightHost) const {
                return leftHost.GetSlotName() > rightHost.GetSlotName();
            }
        };

        NSearchMapParser::TSearchMap SearchMapBySlot(const TString& slotName, const NSaas::TClusterConst* locker, const TString& dc) const {
            NSearchMapParser::TSearchMap searchMap = DoSearchMapBySlot(slotName, locker, dc);
            if (!!slotName) {
                TFunctorSlotsCompare sorter(slotName);
                searchMap.Sort(sorter);
            }
            return searchMap;
        }

        virtual TString GetSearchMapBySlotKey(const TString& /*slotName*/, const TString& /*dc*/) const {
            return "";
        }

        virtual NSearchMapParser::TSearchMap DoSearchMap(const TString& service) const = 0;
        virtual const NSearchMapParser::TSearchMap& SearchMap() const = 0;

        NSearchMapParser::TSearchMap SearchMap(const TString& service) const;

        virtual TVector<NSearchMapParser::TSearchMapHost> GetUsedSlots() const override {
            NSearchMapParser::TSearchMap service = SearchMap("*");
            return service.GetSlots();
        }

        IDeployServiceInfo::TPtr BuildServiceInfo(const TString& serviceName) {
            IDeployServiceInfo::TPtr result = IDeployServiceInfo::TFactory::Construct(GetComponentName());
            result->SetInfo(this, serviceName);
            return result;
        }
    };

}
