#pragma once

#include <library/cpp/testing/gtest/gtest.h>

#include <balancer/server/server.h>
#include <library/cpp/testing/common/network.h>

#include <thread>

class THttpServer {
  public:
    struct TOptions {
        std::function<NSrvKernel::TError (NBalancerServer::THttpRequestEnv&)> Handler;
        TString CertPath;
        TString KeyPath;
    };

    THttpServer(TOptions options)
            : Port_{NTesting::GetFreePort()}
            , SslPort_{NTesting::GetFreePort()}
            , Server_{options.Handler, MakeOptions(options, Port_, SslPort_)}
    {
        auto promise = NThreading::NewPromise();
        ServerThread_ = std::thread{[&, promise]() mutable {
            Server_.Run(&promise);
        }};

        promise.GetFuture().Wait();
    }

    ~THttpServer() {
        Server_.Stop();
        ServerThread_.join();
    }

    ui16 GetPort() {
        return Port_;
    };

    ui16 GetSslPort() {
        return SslPort_;
    };

  private:
    static NBalancerServer::TOptions MakeOptions(const TOptions& options, ui16 port, ui16 sslPort) {
        NBalancerServer::TOptions serverOptions;
        serverOptions.SetPort(port);
        serverOptions.EnableInputStreaming = true;

        if (options.CertPath) {
            serverOptions.SslPort = sslPort;

            TStringBuilder sslConfig;
            sslConfig << "contexts = {\n";
            sslConfig << "  default = {\n";
            sslConfig << "     cert = " << options.CertPath.Quote() << ";\n";
            sslConfig << "     priv = " << options.KeyPath.Quote() << ";\n";
            sslConfig << "     ciphers = \"DEFAULT\";\n";
            sslConfig << "  };\n";
            sslConfig << "};\n";

            serverOptions.SslConfig = sslConfig;
        }

        return serverOptions;
    }

    NTesting::TPortHolder Port_;
    NTesting::TPortHolder SslPort_;
    NBalancerServer::TStandaloneServer Server_;
    std::thread ServerThread_;
};
