#include "balancer_client.h"
#include "internal/request_context.h"

#include <balancer/kernel/http2/client/http2_backend.h>

#include <library/cpp/threading/future/core/future.h>

#include <util/generic/size_literals.h>

namespace {
NModProxy::TProxyConfig MakeProxyConfig(const NHttp::NRequesters::TBalancerClient::TOptions& options) {
    NSrvKernelProto::TBackendConfig::TProto proto;
    auto httpsSettings = proto.mutable_https_settings();
    httpsSettings->set_ciphers("DEFAULT");
    httpsSettings->set_sni_on(true);
#if defined(__linux__)
    httpsSettings->set_ca_file("/etc/ssl/certs/ca-certificates.crt");
#elif defined(__APPLE__)
    httpsSettings->set_ca_file("/etc/ssl/cert.pem");
#endif
    if (options.CertPath) {
        httpsSettings->set_ca_file(options.CertPath);
    }
    // TODO: add ca path "/etc/ssl/certs"

    NModProxy::TProxyConfig config;
    config.BackendConfig = NSrvKernel::TBackendConfig{proto};
    config.BackendConfig.set_backend_timeout(TDuration::Max());
    config.KeepAliveCount = options.KeepAliveCount;
    config.UseSameHttpVersion = true;

    return config;
}
}

namespace NHttp::NRequesters {
TBalancerClient::TBalancerClient(TOptions options)
    : Options_(std::move(options))
    , ProxyConfig_{MakeProxyConfig(Options_)}
    , ConnectionManager_{ProxyConfig_.KeepAliveCount,
                         ProxyConfig_.KeepAliveTimeout.GetOrElse(TDuration::Zero()),
                         ProxyConfig_.BackendConfig.connect_timeout()}
    , Proxy_{ProxyConfig_}
    , StatsManager_{Allocator_}
{
    StatsManager_.SetWorkersCount(1, 0);
    for (size_t i = 0; i < Options_.WorkersCount; i++) {
        Tls_.emplace_back(std::make_unique<TTls>(StatsManager_, ConnectionManager_));
        NSrvKernel::TBackendProtocolFactory backendProtocolFactory;
        backendProtocolFactory.RegisterProtocolImpl<NBalancerClient::THttp2Backend>();
        Tls_.back()->State.AddImpl(backendProtocolFactory, NBalancerClient::THttp2Backend::ProtocolName(), ProxyConfig_.BackendConfig);
    }
}

std::unique_ptr<IRequestContext> TBalancerClient::Send(TRequest request) noexcept {
    size_t index = RandomNumber(Options_.WorkersCount);
    auto context = std::make_unique<TRequestContext>(*this, *Tls_[index], std::move(request));
    context->Run();
    return context;
}
}
