#pragma once

#include "http2_connection.h"
#include "pinger.h"

#include <balancer/kernel/client_request/backend.h>
#include <balancer/kernel/client_request/backend_config.h>
#include <balancer/kernel/connection_manager_helpers/helpers.h>

#include <library/cpp/coroutine/engine/mutex.h>

namespace NBalancerClient {
class THttp2Backend: public NSrvKernel::TBackendProtocolImpl<THttp2Backend> {
    class TState {
        friend class THttp2Backend;

        class TConnectionHolder {
          public:
            TConnectionHolder() = default;
            TConnectionHolder(TState* state, TSimpleSharedPtr<THttp2Connection> connection);
            TConnectionHolder(TConnectionHolder&&) = default;
            TConnectionHolder& operator=(TConnectionHolder&&) = default;

            ~TConnectionHolder();

            THttp2Connection* operator->();
          private:
            TState* State_;
            TSimpleSharedPtr<THttp2Connection> Connection_;
        };

      public:
        TState();

      private:
        NSrvKernel::TErrorOr<TConnectionHolder> GetOrCreateConnection(const NSrvKernel::TBackendConfig& config,
                                                                      const NSrvKernel::TConnDescr& descr,
                                                                      const NSrvKernel::THostInfo& hostInfo) noexcept;
        NSrvKernel::TErrorOr<TSimpleSharedPtr<THttp2Connection>> Connect(const NSrvKernel::TBackendConfig& config,
                                                                         const NSrvKernel::TConnDescr& descr,
                                                                         const NSrvKernel::THostInfo& hostInfo,
                                                                         const TString& addr) noexcept;
        void ReturnConnection(TSimpleSharedPtr<THttp2Connection> connection);
        void Clean();

        TPinger Pinger_;
        THashMap<TString, TSimpleSharedPtr<THttp2Connection>> Connections_;
        THashMap<TString, THolder<TContMutex>> ConnectMutexes_;
        TIntrusiveList<THttp2Connection> UnusedConnections_;
        NSrvKernel::TCoroutine CleanCoroutine_;
    };
  public:
    using TConnectionHolder = TState::TConnectionHolder;
    class TStateHolder {
      public:
        TStateHolder(size_t workersCount);
      private:
        friend class THttp2Backend;

        std::shared_ptr<TState> GetState(size_t workerId);

        TVector<std::weak_ptr<TState>> States_;
    };

    THttp2Backend(const NSrvKernel::TBackendConfig& config, TStateHolder* stateHolder = nullptr);

    NSrvKernel::TErrorOr<TConnectionHolder> GetOrCreateConnection(const NSrvKernel::TConnDescr& descr, const NSrvKernel::THostInfo& hostInfo) noexcept;

    static TStringBuf ProtocolName();

    const TDuration& GetBackendTimeout();
  private:
    const NSrvKernel::TBackendConfig& Config_;
    TStateHolder* StateHolder_;
    std::shared_ptr<TState> State_;
};
}
