#pragma once

#include "algorithm.h"
#include "policy.h"

#include <balancer/kernel/balancing/backend.h>
#include <balancer/kernel/process/config_check.h>

#include <library/cpp/json/json_writer.h>

namespace NSrvKernel {
    class IBackends {
    // Initialization
    // --------------------------------------------------------------------------------
    public:
        virtual ~IBackends() = default;
        virtual void Init(IWorkerCtl*) {}
        virtual void ProcessPolicyFeatures(const TPolicyFeatures&) {}
        void SetCheckParameters(const TBackendCheckParameters& checkParams) noexcept {
            CheckParameters_ = checkParams;
        }

        virtual bool CanUpdate() const noexcept {
            return false;
        }
        virtual THolder<IBackends> Update(const NSrvKernel::TModuleParams&) const {
            return nullptr;
        }
    // --------------------------------------------------------------------------------


    public:
        virtual void DumpBackends(NJson::TJsonWriter&, const IWorkerCtl&) const noexcept = 0;
        virtual void DumpBalancingState(NJson::TJsonWriter&, const IWorkerCtl&) const noexcept = 0;
        virtual void DumpWeightsFileTags(NJson::TJsonWriter&, const IWorkerCtl&) const noexcept = 0;

    // Functionality
    // --------------------------------------------------------------------------------
    public:
        virtual void ChangeNotReadySet(TIntrusivePtr<TNotReadyBackendSet>) const noexcept {}

        virtual THolder<IAlgorithm> ConstructAlgorithm(const TStepParams& params) noexcept = 0;
        virtual THolder<IAlgorithm> InvalidateAlgorithm(IAlgorithm& /*algorithm*/, const THashSet<IBackend*>& /*backendsInUse*/) noexcept {
            return nullptr;
        }

        virtual bool IsHashing() const noexcept {
            return false;
        }

    public:
        const TVector<TBackendDescriptor::TRef>& BackendDescriptors() const noexcept {
            return BackendDescriptors_;
        }

        void Add(THolder<TBackendDescriptor> b) {
            BackendDescriptors_.emplace_back(std::move(b));
        }

        void Add(TBackendDescriptor::TRef b) {
            BackendDescriptors_.push_back(std::move(b));
        }

        size_t Size() const noexcept {
            return BackendDescriptors_.size();
        }

        virtual TBackendCheckResult CheckBackends(IWorkerCtl& proc, bool runtimeCheck) noexcept;
    protected:
        TError CheckBackend(IWorkerCtl& proc, TBackendDescriptor::TRef backend, const TMaybe<TRequest>& request = {}) const noexcept;
        TBackendCheckResult VisitBackends(IWorkerCtl& proc, bool runtimeCheck, std::function<void(IWorkerCtl&, TBackendDescriptor::TRef, TBackendCheckResult&, bool)> func) const noexcept;
        void PostProcessCheckResult(TBackendCheckResult& result, const TBackendCheckParameters& parameters, size_t totalCount, size_t failuresCount) noexcept;
        bool ShouldSkipCheck(const TBackendCheckParameters& parameters) const noexcept;
        TBackendCheckParameters ActualCheckParameters(IWorkerCtl& proc) const noexcept;

    // Destruction
    // --------------------------------------------------------------------------------
    public:
        virtual void Dispose(IWorkerCtl*) {}
    // --------------------------------------------------------------------------------

    protected:
        TBackendCheckParameters CheckParameters_;
    private:
        TVector<TBackendDescriptor::TRef> BackendDescriptors_;
        std::atomic<TBackendCheckResult::EStatus> LastCheckStatus_ = TBackendCheckResult::EStatus::Skipped;
    };

    struct TBackendsUID: public TNonCopyable {
    public:
        TBackendsUID();
    public:
        using TValue = size_t;
        const TValue Value;
    };
}
