#pragma once

#include <balancer/kernel/helpers/errors.h>
#include <balancer/kernel/module/module_face.h>
#include <util/datetime/base.h>
#include <util/generic/ptr.h>


namespace NSrvKernel {
    class IModule;
    class TModuleParams;

    struct TNotReadyBackendSet : public THashSet<TString>, public TThrRefBase {
        using THashSet::THashSet;
    };

    class TBackendDescriptor {
    public:
        using TRef = TAtomicSharedPtr<TBackendDescriptor>;

        TBackendDescriptor(TString name, THolder<IModule> mod, double weight = 1.0);

        TBackendDescriptor(TModuleParams mp, const TString& name);

        double Weight() const noexcept {
            return Weight_;
        }

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

        IModule* Module() const noexcept {
            return Module_.Get();
        }

        bool Enabled() const noexcept {
            return Enabled_.load();
        }

        void SetEnabled(bool value) noexcept {
            Enabled_.store(value);
        }

        bool Degraded() const noexcept {
            return Degraded_.load();
        }

        void SetDegraded(bool value) noexcept {
            Degraded_.store(value);
        }

        double WeightFromPing() const noexcept {
            return WeightFromPing_.load();
        }

        void SetWeightFromPing(double value) noexcept {
            WeightFromPing_.store(value);
        }

        TString GroupName() const noexcept {
            with_lock(Mutex_) {
                return GroupName_;
            }
        }

        void SetGroupName(TString groupName) noexcept {
            with_lock(Mutex_) {
                GroupName_ = std::move(groupName);
            }
        }

        void Init(IWorkerCtl* process);
        void Dispose(IWorkerCtl* process);

    private:
        TString Name_;
        THolder<IModule> Module_;
        double Weight_ = 1.;
        std::atomic<bool> Enabled_ = true;
        std::atomic<double> WeightFromPing_{1.};
        std::atomic<bool> Degraded_ = false;
        TMutex Mutex_;
        TString GroupName_;
        TModuleList Modules_;
    };

    class IBackend {
    public:
        virtual ~IBackend() = default;

        virtual IModule* Module() const noexcept = 0;
        virtual void OnCompleteRequest(const TDuration& answerTime) noexcept = 0;
        virtual void OnFailRequest(const TError& error, const TDuration& answerTime) noexcept = 0;

        // Those methods should be removed from this interface.
        virtual const TString& Name() const noexcept = 0;
        virtual bool Enabled() const noexcept = 0;
        virtual bool Degraded() const noexcept = 0;
    };
}
