#pragma once

#include <balancer/kernel/balancer/backends.h>
#include <balancer/kernel/module/node.h>

namespace NSrvKernel {

    template <>
    class INodeHandle<IBackends> {
    public:
        virtual ~INodeHandle() noexcept = default;

        const TString& Name() const noexcept {
            return DoName();
        }

        THolder<IBackends> Construct(const TModuleParams& mp, const TBackendsUID& uid) const {
            return THolder<IBackends>(DoConstruct(mp, uid));
        }
    private:
        virtual const TString& DoName() const /* noexcept */ = 0;
        virtual IBackends* DoConstruct(const TModuleParams& mp, const TBackendsUID& uid) const = 0;
    };

    NODEBASE(IBackends) {
    private:
        class THandle: public INodeHandle<IBackends> {
        private:
            const TString& DoName() const noexcept override {
                return Name_;
            }

            IBackends* DoConstruct(const TModuleParams& mp, const TBackendsUID& uid) const override {
                return TGenericNode<IBackends, T, N>::Construct(mp, uid);
            }
        private:
            TString Name_{N};
        };

    private:
        static inline IBackends* Construct(const TModuleParams& mp, const TBackendsUID& uid) {
            return new T(mp, uid);
        }

    public:
        static inline INodeHandle<IBackends>* Handle() {
            return Singleton<THandle>();
        }
    };

    const INodeFactory<IBackends>* CommonBackends();

    template <class T, class Tls, const char* N>
    class TBackendsWithTLS : public TGenericNode<IBackends, T, N> {
    public:
        TBackendsWithTLS() = delete;
        explicit TBackendsWithTLS(const TModuleParams& mp)
            : Tls_(mp.Control->GetCountOfChildren() + 1)
        {}

        void Init(IWorkerCtl* process) final {
            Y_VERIFY(!Tls_[process->WorkerId()].Get());
            Tls_[process->WorkerId()] = std::move(DoInit(process));
        }

        virtual void DumpBackends(NJson::TJsonWriter& out, const IWorkerCtl& process) const noexcept final {
            Y_VERIFY(Tls_[process.WorkerId()].Get());
            DumpBackends(out, *Tls_[process.WorkerId()]);
        }

        virtual void DumpBalancingState(NJson::TJsonWriter& out, const IWorkerCtl& process) const noexcept final {
            Y_VERIFY(Tls_[process.WorkerId()].Get());
            DumpBalancingState(out, *Tls_[process.WorkerId()]);
        }

        virtual void DumpWeightsFileTags(NJson::TJsonWriter& out, const IWorkerCtl& process) const noexcept final {
            Y_VERIFY(Tls_[process.WorkerId()].Get());
            DumpWeightsFileTags(out, *Tls_[process.WorkerId()]);
        }

        Tls& GetTls(const IWorkerCtl& process) const noexcept {
            Y_VERIFY(Tls_[process.WorkerId()].Get());
            return *Tls_[process.WorkerId()];
        }

        void Dispose(IWorkerCtl* process) final {
            Y_VERIFY(Tls_[process->WorkerId()].Get());
            DoDispose(process, *Tls_[process->WorkerId()]);
            Tls_[process->WorkerId()].Destroy();
        }

    private:
        virtual THolder<Tls> DoInit(IWorkerCtl*) noexcept = 0;
        virtual void DoDispose(IWorkerCtl*, Tls&) noexcept {};
        virtual void DumpBackends(NJson::TJsonWriter&, const Tls&) const noexcept = 0;
        virtual void DumpBalancingState(NJson::TJsonWriter&, const Tls&) const noexcept {}
        virtual void DumpWeightsFileTags(NJson::TJsonWriter&, const Tls&) const noexcept {}

    private:
        TVector<THolder<Tls>> Tls_;
    };
}

#define BACKENDS(X) NODEIMPL(IBackends, TBackends, X)

#define BACKENDS_TLS(X) \
namespace { \
    namespace N ## X { \
        constexpr char NAME[] = #X; \
        struct TTls; \
        struct TBackends; \
    } \
    using namespace N ## X; \
} \
struct N ## X::TTls

#define BACKENDS_WITH_TLS(X) \
struct N ## X::TBackends final : public TBackendsWithTLS<TBackends, TTls, NAME>, public NConfig::IConfig::IFunc
