#pragma once

#include <saas/library/persqueue/configuration/proto/description.pb.h>
#include <saas/library/persqueue/configuration/service/modify_manager/service_meta/meta.h>
#include <saas/library/persqueue/configuration/service/modify_manager/proto/changes.pb.h>
#include <saas/library/persqueue/configuration/service/modify_manager/components.h>

#include <util/generic/vector.h>
#include <util/generic/string.h>

namespace NSaasLB {

    class TServiceMetaManager;
    class TLogbrokerCluster;
    class ILocksCluster;
    class TReadRule;
    class TRemoteMirrorRule;

    enum class EPermission;

    class TModifyManager {
    private:
        class TStageMonitor {
        public:
            TStageMonitor(TModifyManager* manager)
                : Manager(manager)
            {
                Manager->StartNextStage();
            }
            ~TStageMonitor() {
                Manager->StartNextStage();
            }
        private:
            TModifyManager* Manager;
        };

    public:

        TModifyManager(
            const TString& ns,
            const TString& name,
            const TString& ctype,
            const TString& lbToken,
            const std::optional<TString>& lbRemoteMirrorRulesToken,
            std::optional<TServiceConfig> serviceConfig = {}
        );

        ~TModifyManager();

        TServiceDescription Describe();

        TServiceChanges GetChanges() const;
        void Apply();

        // Service meta
        const TServiceConfig& GetServiceConfig() const;
        void SetServiceConfig(const TServiceConfig& config);
        void RemoveServiceConfig();

        // Logbroker
        TVector<TString> GetConsumers(const TString& path, ELogbrokerKind kind = ELogbrokerKind::origin);
        TVector<TString> GetTopics(const TString& path, ELogbrokerKind kind = ELogbrokerKind::origin);
        TVector<TReadRule> GetReadRules(const TString& entity, ELogbrokerKind kind = ELogbrokerKind::origin);
        TVector<TRemoteMirrorRule> GetRemoteMirrorRules(const TString& topic);
        NLogBroker::DescribePathResult DescribePath(const TString& path, ELogbrokerKind kind = ELogbrokerKind::origin);

        TVector<TString> GetYtDeliveries(const TString& topicPath, ELogbrokerKind kind = ELogbrokerKind::origin);
        TVector<TString> GetSaasYtDeliveries(const TString& topicPath, ELogbrokerKind kind = ELogbrokerKind::origin);

        TVector<TString> GetConsumersFor(const TString& topic, ELogbrokerKind kind = ELogbrokerKind::origin);

        void CreateConsumers(const TVector<TString>& consumers, ELogbrokerKind kind = ELogbrokerKind::origin);
        void CreateTopics(const TVector<TString>& topics, ELogbrokerKind kind = ELogbrokerKind::origin, bool authRequired = false);
        void CreateReadRules(const TVector<TReadRule>& readRules, ELogbrokerKind kind = ELogbrokerKind::origin);
        void CreateRemoteMirrorRules(const TVector<TRemoteMirrorRule>& rules);
        void AddYtDelivery(const TString& topicPath, const TString& ytDelivery, ELogbrokerKind kind = ELogbrokerKind::origin);

        void RemoveConsumers(const TVector<TString>& consumers, ELogbrokerKind kind = ELogbrokerKind::origin);
        void RemoveTopics(const TVector<TString>& topics, ELogbrokerKind kind = ELogbrokerKind::origin);
        void RemoveReadRules(const TVector<TReadRule>& readRules, ELogbrokerKind kind = ELogbrokerKind::origin);
        void RemoveRemoteMirrorRules(const TVector<TRemoteMirrorRule>& rules);
        void RemoveYtDelivery(const TString& topicPath, const TString& ytDelivery, ELogbrokerKind kind = ELogbrokerKind::origin);

        void GrantPermissions(
            const TString& path,
            const TVector<EPermission>& permissions,
            const TVector<TString>& subjects,
            ELogbrokerKind kind = ELogbrokerKind::origin
        );
        void RevokePermissions(
            const TString& path,
            const TVector<EPermission>& permissions,
            const TVector<TString>& subjects,
            ELogbrokerKind kind = ELogbrokerKind::origin
        );

        // LockInfo
        TVector<TString> GetLocks(const TString& path);
        TLockDescription DescribeLock(const TString& path, const TString& consumer);

        void AddLocks(const TString& path, const TVector<TString>& consumers);
        void RemoveLocks(const TString& path, const TVector<TString>& consumers);

    private:
        THolder<TLogbrokerCluster>& GetLogbrokerCluster(ELogbrokerKind kind);
        void AddStage(const TStage& stage);
        void StartNextStage();

    private:
        TServiceChanges Changes;

        THolder<TServiceMetaManager> ServiceMeta;
        THolder<TLogbrokerCluster> Logbroker;
        THolder<TLogbrokerCluster> LogbrokerMirror;
        THolder<ILocksCluster> Locks;

        ui32 ServiceMetaChangesCount = 0;
        ui32 LogbrokerChangesCount = 0;
        ui32 LogbrokerMirrorChangesCount = 0;
        ui32 LocksChangesCount = 0;
    };

}
