#pragma once

#include "options.h"

#include <saas/library/persqueue/configuration/namespace/namespace.h>
#include <saas/library/persqueue/configuration/service/service.h>


namespace NSaasLB {

    enum class EPermissionModiftyType {
        GRANT,
        REVOKE
    };

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

    public:
        virtual ~ICommand() = default;
        virtual void Execute() = 0;
    };

    class IModifyCommand : virtual public ICommand {
    public:
        using TOptionsType = TModifyOptions;
    public:
        IModifyCommand(const TOptionsType& options);
        void Execute() override;

    protected:
        virtual void DoExecute() = 0;
        virtual void DoApply() = 0;
        virtual void PrintChanges() const = 0;
        bool UserConfirm() const;
        void Apply();

    protected:
        bool DryRun;
        bool SkipConfirm;
    };

    class INamespaceCommand : virtual public ICommand {
    public:
        using TOptionsType = TNamespaceOptions;
    public:
        INamespaceCommand(const TOptionsType& options, const TString& name);
        INamespaceCommand(const TOptionsType& options);

    protected:
        TNamespaceManager NamespaceManager;
    };

    class TDescribeNamespaceCommand : public INamespaceCommand {
    public:
        TDescribeNamespaceCommand(const TOptionsType& options);

    protected:
        void Execute() override;
    };

    class IUpdateNamespaceCommand
        : public INamespaceCommand
        , public IModifyCommand
    {
    public:
        using TOptionsType = TCommonModifyNamespaceOptions;

    public:
        IUpdateNamespaceCommand(const TOptionsType& options, const TString& name);
        IUpdateNamespaceCommand(const TOptionsType& options);

    protected:
        void PrintChanges() const override;
        void DoApply() override;
    };

    class TRemoveNamespaceCommand : public IUpdateNamespaceCommand {
    public:
        TRemoveNamespaceCommand(const TOptionsType& options);
        void DoExecute() override;
    };

    class TCreateNamespaceCommand : public IUpdateNamespaceCommand {
    public:
        using TOptionsType = TModifyNamespaceOptions;

    public:
        TCreateNamespaceCommand(const TOptionsType& options);
        void DoExecute() override;

    protected:
        TCreateNamespaceCommand(const TOptionsType& options, const TString& name);

    private:
        static TNamespaceConfig CreateConfig(const TOptionsType& options);

    protected:
        TString Name;
        TNamespaceConfig Config;
    };

    class TUpdateNamespaceCommand : public TCreateNamespaceCommand {
    public:
        TUpdateNamespaceCommand(const TOptionsType& options);
        void DoExecute() override;
    };

    class IServiceCommand : virtual public ICommand {
    public:
        using TOptionsType = TServiceOptions;
    public:
        IServiceCommand(
            const TOptionsType& options,
            std::optional<TServiceConfig> serviceConfig = {},
            std::optional<TVector<TString>> shards = {},
            std::optional<ui32> replicas = {},
            std::optional<TString> gencfgTag = {},
            bool noRemove = true,
            bool forceRemove = false
        );

    protected:
        THolder<NSaasLB::TServiceConfiguration> ServiceConfiguration;
    };

    class IModifyServiceCommand
        : public IServiceCommand
        , public IModifyCommand
    {
    public:
        using TOptionsType = TModifyServiceOptions;
    public:
        IModifyServiceCommand(
            const TOptionsType& options,
            std::optional<TServiceConfig> serviceConfig = {},
            std::optional<TVector<TString>> shards = {},
            std::optional<ui32> replicas = {}
        );

    protected:
        void PrintChanges() const override;
        void DoApply() override;
    };

    class TUpdateTopicsCommand : public IModifyServiceCommand {
    public:
        using TOptionsType = TUpdateTopicsOptions;
    public:
        TUpdateTopicsCommand(const TOptionsType& options);

        void DoExecute() override;
    };

    class TUpdateConsumersCommand : public IModifyServiceCommand {
    public:
        using TOptionsType = TUpdateConsumersOptions;
    public:
        TUpdateConsumersCommand(const TOptionsType& options);

        void DoExecute() override;
    };

    class TUpdateLocksCommand : public TUpdateConsumersCommand {
    public:
        using TUpdateConsumersCommand::TUpdateConsumersCommand;

        void DoExecute() override;
    };

    class TCreateServiceCommand : public IModifyServiceCommand {
    public:
        using TOptionsType = TCreateServiceOptions;
    public:
        TCreateServiceCommand(const TOptionsType& options);

        void DoExecute() override;

    private:
        TServiceConfig CreateServiceConfig(const TOptionsType& options) const;

    private:
        TVector<ui32> TvmWrite;
        TVector<ui32> TvmRead;
    };

    class TRemoveServiceCommand : public IModifyServiceCommand {
    public:
        using TOptionsType = TModifyServiceOptions;
    public:
        TRemoveServiceCommand(const TOptionsType& options);

        void DoExecute() override;
    };

    class TDescribeServiceCommand : public IServiceCommand {
    public:
        using TOptionsType = TDescribeServiceOptions;
    public:
        TDescribeServiceCommand(const TOptionsType& options);

        void Execute() override;

    private:
        bool ShowLocks;
    };

    template<EPermissionModiftyType type, EServicePermission permission>
    class TModifyPermissionsCommand : public IModifyServiceCommand {
    public:
        using TOptionsType = TModifyPermissionsOptions;
    public:
        TModifyPermissionsCommand(const TOptionsType& options)
            : IModifyServiceCommand(options)
            , TvmIds(options.GetTvmIds())
        {}

        void DoExecute() override {
            static_assert(type == EPermissionModiftyType::GRANT || type == EPermissionModiftyType::REVOKE);
            switch (type) {
                case EPermissionModiftyType::GRANT: {
                    ServiceConfiguration->GrantPermission(permission, TvmIds);
                    break;
                }
                case EPermissionModiftyType::REVOKE: {
                    ServiceConfiguration->RevokePermission(permission, TvmIds);
                    break;
                }
                default: {
                    ythrow yexception() << "Unsupported permissions modify type: " << type;
                }
            }
        }

    protected:
        TVector<ui32> TvmIds;
    };
}
