#pragma once

#include <saas/deploy_manager/config/config.h>
#include <saas/library/daemon_base/protos/status.pb.h>
#include <saas/library/storage/abstract.h>
#include <saas/util/cluster/cluster.h>
#include <library/cpp/json/writer/json_value.h>
#include <google/protobuf/text_format.h>
#include <util/datetime/base.h>
#include <util/generic/string.h>
#include <util/generic/hash_set.h>
#include <util/generic/vector.h>

class TNannyModule;
class TRequestWizardModule;
class TServices20Module;
class TJugglerModule;
class TGolovanModule;
class TSDModule;

namespace NRTYCluster{
    class TCluster;
}

namespace NRTYDeploy {
    class TSlotChecker;
    class TResourcesManager;
    class TCacheModule;
}

namespace NRTYDeploy {
    using NSaas::TStorageOptions;
    using NSaas::TAbstractLock;
    using NSaas::IVersionedStorage;

    struct TRequestContext {
        TRequestContext()
            : Start(Now())
        {}

        TString Text;
        TInstant Start;
        ui64 Id;
    };

    struct TDequeueInfo {
        TString Id;
        TString FullId;
        TString SubQueue;
        TString FullPath;
        TAbstractLock::TPtr Lock;
    };

    /**
     * @brief Interface for reable shared between multiple instances task queue
     *
     */
    class IDistrTasksQueue {
    public:
        virtual ~IDistrTasksQueue() {}

        virtual bool Ack(const TDequeueInfo& info) const = 0;
        virtual bool Dequeue(const TString& queueName, TDequeueInfo& info) const = 0;
        virtual bool Enqueue(const TString& queueName, const TString& subQueueName, const TString& id) const = 0;
        virtual bool GetTasks(const TString& queueName, const TString& subQueueRegEx, TVector<TString>& tasks) const = 0;
    };

    class IUserAccessControl {
    public:
        typedef THashSet<TString> TServicesSet;

        struct TUsedResources {
            TServicesSet Services;
        };

        struct TPermissions {
            TUsedResources Resources;
            TServicesSet MyServices;
            TString User;
        };
    public:
        virtual ~IUserAccessControl() {}

        virtual const TPermissions& GetPermissions() const = 0;
        virtual TString GetCookie(const TString& user) const = 0;
        virtual TString GetUserConfig(const TString& user, bool group) const = 0;
        virtual void SetUserConfig(const TString& config, const TString& user, bool group) = 0;
    };

    class IControllersChecker {
    public:
        struct TPingInfo {
            ui32 Success;
            ui32 Fails;

            TPingInfo();
            TPingInfo(const NController::TPingInfoByTime& info);
            NController::TPingInfoByTime SerializeToProto() const;
            NJson::TJsonValue SerializeToJson() const;
        };

        struct TControllerStatus {
            TInstant FirstPing;
            TInstant LastPing;
            TInstant LastReply;
            NController::TSlotStatus Status;
            TString Info;
            TMap<ui32, TPingInfo> PingStatusesByTime;

            TControllerStatus();
            TControllerStatus(const NController::TSlotChecker& value);

            void AddInfoByTime();
            TDuration GetNotAnsweredTime() const;

            NController::TSlotChecker SerializeToProto() const;
            NJson::TJsonValue SerializeToJson() const;

            void ReadControllerReply(const NJson::TJsonValue& value);
        };

        struct TSlotInfo {
            IControllersChecker::TControllerStatus ControllerStatus;
            bool IsNormalSlot = false;
            bool LoadedFromStorage = false;

            TSlotInfo() {}

            TSlotInfo(const IControllersChecker::TControllerStatus& status, bool isNormalSlot, bool loadedFromStorage)
                : ControllerStatus(status)
                , IsNormalSlot(isNormalSlot)
                , LoadedFromStorage(loadedFromStorage)
            {}
        };

        using TSlotsStates = TMap<TString, TSlotInfo>;

        IControllersChecker(const TDeployManagerConfig::TCheckerConfig& config);
        void CheckSlots(const TMap<TString, NRTYCluster::TSlotData>& slots, TSlotsStates& states) const;
        virtual TSlotInfo CheckSlot(const NRTYCluster::TSlotData& sd) const = 0;
        virtual TSlotInfo UpdateSlot(const NRTYCluster::TSlotData& sd, const NJson::TJsonValue& serverStatusGlobal) const = 0;
        virtual TSlotInfo FailSlot(const NRTYCluster::TSlotData& sd) const = 0;
        virtual ~IControllersChecker() {}

        class TFilter: public NRTYCluster::ISlotsFilter {
        public:
            TFilter(const IControllersChecker* checker);
            virtual TMap<TString, NRTYCluster::TDatacenter> Apply(const TMap<TString, NRTYCluster::TDatacenter>& dcs) const override;
        private:
            const IControllersChecker* Checker;
        };

    private:
        class TCheckingTask;
    protected:
        const TDeployManagerConfig::TCheckerConfig& Config;
    };

    /**
     * @brief Holder of common information about infractructure
     *
     */
    class ICommonData {
    public:
        virtual ~ICommonData() {}
        virtual const TString& GetDeployManagerBalanserHost() const = 0;
        virtual ui16 GetDeployManagerBalanserPort() const = 0;
        virtual const TString& GetDeployManagerBalanserUriPrefix() const = 0;
        virtual IControllersChecker& GetControllersChecker() = 0;
        virtual const IVersionedStorage& GetStorage() const = 0;
        virtual IVersionedStorage& GetStorage() = 0;
        virtual TNannyModule& GetNannyModule() const = 0;
        virtual TSDModule& GetSDModule() const = 0;
        virtual TJugglerModule& GetJugglerModule() const = 0;
        virtual TGolovanModule& GetGolovanModule() const = 0;
        virtual TRequestWizardModule& GetRequestWizardModule() = 0;
        virtual IDistrTasksQueue& GetQueue() = 0;
        virtual const NRTYDeploy::TResourcesManager& GetResourcesManager() const = 0;
        virtual TServices20Module& GetServices20Module() = 0;
        virtual NRTYDeploy::TCacheModule& GetCacheModule() = 0;
        virtual const TDeployManagerConfig& GetConfig() const = 0;
        virtual void SafeAddAndOwn(TAutoPtr<IObjectInQueue> obj) = 0;
    };

};
