#pragma once

#include "base_algorithm.h"

#include <balancer/kernel/balancing/per_worker_backend.h>
#include <balancer/kernel/balancer/algorithm.h>
#include <balancer/kernel/balancing/backend.h>
#include <balancer/kernel/balancer/policy.h>
#include <balancer/kernel/pinger/pinger.h>

#include <balancer/kernel/http/parser/http.h>

namespace NSrvKernel {

class IBackends;
template <class> class INodeHandle;


namespace NModBalancer::Nhashing {
    constexpr char NAME[]="hashing";
    struct TTls;
    struct TBackends;
}  // namespace NModBalancer::Nhashing

namespace NModBalancer::NHashing {

class TBackend : public TPerWorkerBaseBackend {
public:
    TBackend(TBackendDescriptor::TRef descr, Nhashing::TTls& tls);

    TBackend(TBackend&&) noexcept = default;

    ~TBackend() noexcept override = default;

    void PrintInfo(NJson::TJsonWriter& out) const noexcept;

private:
    Nhashing::TTls& Tls_;
};

class THashingAlgorithm : public TAlgorithmWithRemovals {
public:
    THashingAlgorithm(Nhashing::TTls& tls, const TStepParams& params, const TVector<double>& weights) noexcept;

    void RemoveSelected(IBackend* backend) noexcept override {
        DoRemove(backend);
    }

    void Reset() noexcept override {
        FillWeights();
    }

    IBackend* Next() noexcept override;

    IBackend* NextByName(TStringBuf name, bool allowZeroWeights) noexcept override;

private:
    void FillWeights() noexcept;

    bool BackendWasSelected(IBackend* backend) noexcept;

    void DoRemove(IBackend* backend) noexcept;

public:
    Nhashing::TTls& Tls_;
    const TVector<double>& Weights_;
    IBackend* LastDisabledBackend_ = nullptr;
    TRequestHash Hash_;
};

INodeHandle<IBackends>* Handle();

}  // namespace NModBalancer::NHashing

namespace NModBalancer::Nhashing {
    struct TTls {
        explicit TTls(const TPingerConfigUnparsed& config)
            : PingerConfig(config)
        {}

        THashMap<TString, NHashing::TBackend*> NamedBackends;
        TDeque<NHashing::TBackend> Backends;
        TInstant LastRequestTime;
        TPingerConfig PingerConfig;
        bool PingersStarted = false;
        TDeque<TPinger> Pingers;
    };
}  // namespace NModBalancer::Nhashing

}  // namespace NSrvKernel
