#include "connection_manager.h"

namespace {
NConnectionManager::TConnectionManager::TOptions MakeOptions() {
    NConnectionManager::TConnectionManager::TOptions options;
    options.WorkersCount = 1;
    return options;
}
}

namespace NHttp::NRequesters {
TConnectionManager::TConnectionManager(size_t keepAliveCount, TDuration keepAliveTimeout, TDuration connectTimeout)
    : Worker_{"NHttp::TConnectionManager"}
    , ConnectionManager_{MakeIntrusive<NConnectionManager::TConnectionManager>(MakeOptions())}
    , ConnectionManagerRuntime_{Worker_.Execute([this](){ return ConnectionManager_->StartRuntime(RunningCont()->Executor()); })}
    , KeepAliveCount_{keepAliveCount}
    , KeepAliveTimeout_{keepAliveTimeout}
    , ConnectTimeout_{connectTimeout}
{}

TConnectionManager::~TConnectionManager() {
    Worker_.Execute([runtime = std::move(ConnectionManagerRuntime_)]() mutable {
        runtime.Reset();
    });
}

THolder<NModProxy::TKeepAliveData> TConnectionManager::TryPullConnection(const NSrvKernel::TConnDescr& descr, const NSrvKernel::THostInfo& hostInfo) {
    if (!KeepAliveCount_) {
        return {};
    }

    TString sni;
    if (descr.AttemptsHolder->UseEncryption()) {
        sni = StripString(descr.Request->Headers().GetFirstValue("host"));
    }
    auto pool = GetPool(hostInfo.CachedIp, hostInfo.Port, sni);
    return pool->TryPullConnection().BuildKeepaliveData(RunningCont()->Executor(), pool->PollMode());;
}

void TConnectionManager::StoreConnection(THolder<NModProxy::TKeepAliveData> connection) {
    GetPool(connection->ConcreteAddress().Ip().ToString(), connection->ConcreteAddress().Port(), connection->SniHost())->AddConnection(connection);
}

NConnectionManager::TConnectionPool* TConnectionManager::GetPool(const TString& ip, ui16 port, const TString& sniHost) {
    std::tuple<TString, ui16, TString> key{ip, port, sniHost};
    with_lock(Mutex_) {
        auto& pool = PoolMap_[key];
        if (!pool) {
            pool = ConnectionManager_->AddConnectionPool(Counter_++,
                                                         KeepAliveCount_,
                                                         NSrvKernel::PM_EDGE_TRIGGERED,
                                                         KeepAliveTimeout_,
                                                         ConnectTimeout_);
        }
        return pool;
    }
}
}
