#pragma once

#include <logbroker/public/api/grpc/config_manager.grpc.pb.h>
#include <ydb/public/api/protos/draft/persqueue_common.pb.h>

#include <util/generic/ptr.h>
#include <util/generic/hash_set.h>
#include <util/generic/vector.h>
#include <util/string/cast.h>
#include <util/datetime/base.h>

namespace NSaasLB {

    const TVector<NPersQueueCommon::ECodec> AllSupportedCodecs = {
        NPersQueueCommon::ECodec::RAW,
        NPersQueueCommon::ECodec::GZIP,
        NPersQueueCommon::ECodec::LZOP,
        // NPersQueueCommon::ECodec::ZSTD, is disabled due to SAAS-5911
    };

    enum class EEntityType {
        account,
        directory,
        topic,
        consumer
    };

    TString GetCorrectEntityPath(const TString& originalPath);

    class IEntity {
    public:
        using TPtr = TAtomicSharedPtr<IEntity>;

    public:
        IEntity(const TString& path);
        virtual ~IEntity() = default;
        virtual IEntity::TPtr Clone() const = 0;

        void SetPath(const TString& path);
        TString GetPath() const;

        virtual EEntityType GetType() const = 0;
        virtual NLogBroker::DescribePathResult GetDescribed() const = 0;

        virtual NLogBroker::SingleModifyRequest GetAddCommand() const = 0;
        virtual NLogBroker::SingleModifyRequest GetRemoveCommand() const = 0;
        virtual NLogBroker::SingleModifyRequest GetModifyCommand() const;

        virtual bool Equal(const IEntity& entity) const;
        bool operator== (const IEntity& entity) const;
        bool operator!= (const IEntity& entity) const;

    private:
        TString Path;
    };

    class IEntityWithCodec : public IEntity {
    private:
        THashSet<NPersQueueCommon::ECodec> Codecs;

    public:
        IEntityWithCodec(const TString& path, TVector<NPersQueueCommon::ECodec> codecs = AllSupportedCodecs);
        IEntityWithCodec(const NLogBroker::DescribePathResult& describe);

        void AddCodec(NPersQueueCommon::ECodec codec);
        void RemoveCodec(NPersQueueCommon::ECodec codec);

        TVector<NPersQueueCommon::ECodec> GetCodecs() const;

    protected:
        template <class T>
        void FillProperties(T& proto) const {
            proto.mutable_supported_codecs()->set_user_defined(GetCodecsString());
        }

    private:
        TString GetCodecsString() const;
    };

    class TConsumer : public IEntityWithCodec {
    public:
        TConsumer(const TString& path);
        TConsumer(const NLogBroker::DescribePathResult& describe);

        IEntity::TPtr Clone() const override;
        EEntityType GetType() const override;

        NLogBroker::DescribePathResult GetDescribed() const override;
        NLogBroker::SingleModifyRequest GetAddCommand() const override;
        NLogBroker::SingleModifyRequest GetRemoveCommand() const override;
        NLogBroker::SingleModifyRequest GetModifyCommand() const override;

    protected:
        void FillProperties(NLogBroker::ConsumerProperties& props) const;
    };

    class TTopic : public IEntityWithCodec {
    private:
        ui32 PartitionsCount = 1;
        TDuration RetentionPeriod = TDuration::Hours(36);
        bool AuthRequired = false;

    public:
        TTopic(const TString& path, ui32 partitionsCount = 1, TDuration retentionPeriod = TDuration::Hours(36));
        TTopic(const NLogBroker::DescribePathResult& describe);
        IEntity::TPtr Clone() const override;

        void SetPartitionsCount(ui32 partitionsCount);
        void SetRetentionPeriod(TDuration retentionPeriod);
        void SetAuthRequired(bool authRequired);
        TDuration GetRetentionPeriod() const;
        ui32 GetPartitionsCount() const;
        bool GetAuthRequired() const;

        EEntityType GetType() const override;
        NLogBroker::DescribePathResult GetDescribed() const override;

        NLogBroker::SingleModifyRequest GetAddCommand() const override;
        NLogBroker::SingleModifyRequest GetRemoveCommand() const override;
        NLogBroker::SingleModifyRequest GetModifyCommand() const override;

    protected:
        void FillProperties(NLogBroker::TopicProperties& props) const;
    };

    class TDirectory : public IEntity {
    public:
        using IEntity::IEntity;
        TDirectory(const ::NLogBroker::DescribePathResult& describe);
        IEntity::TPtr Clone() const override;

        EEntityType GetType() const override;
        NLogBroker::DescribePathResult GetDescribed() const override;

        NLogBroker::SingleModifyRequest GetAddCommand() const override;
        NLogBroker::SingleModifyRequest GetRemoveCommand() const override;
    };

    class TAccount : public IEntity {
    public:
        using IEntity::IEntity;
        TAccount(const ::NLogBroker::DescribePathResult& describe);
        IEntity::TPtr Clone() const override;

        EEntityType GetType() const override;
        NLogBroker::DescribePathResult GetDescribed() const override;

        NLogBroker::SingleModifyRequest GetAddCommand() const override;
        NLogBroker::SingleModifyRequest GetRemoveCommand() const override;
    };

}
