#include "server.h"

#include <library/cpp/http/server/http.h>
#include <library/cpp/logger/global/global.h>

TSearchServerBase::TSearchServerBase(const THttpServerOptions& options, size_t servers)
    : RunningCounter(0)
{
    VERIFY_WITH_LOG(servers > 0, "Incorrect count of HTTP servers: %lu", servers);
    HttpServers.reserve(servers);
    for (ui32 i = 0; i < servers; i++) {
        HttpServers.push_back(MakeHolder<THttpServer>(this, options));
    }
}

TSearchServerBase::~TSearchServerBase() {
    Stop();
}

bool TSearchServerBase::ClientConnected()
{
    return true;
}

void TSearchServerBase::Start()
{
    TGuard<TMutex> g(StateLock);
    if (IsRunningUnsafe(g)) {
        return ;
    }
    NOTICE_LOG << "Starting " << HttpServers.size() << " HTTP servers ..." << Endl;
    for (auto &server : HttpServers) {
        bool status = server->Start();
        VERIFY_WITH_LOG(status, "Error running server on port %d : %s", server->Options().Port, server->GetError());
    }
    // HttpServers::Start() completes only after OnListenStart cb is called, all threads should be running now
    VERIFY_WITH_LOG(IsRunningUnsafe(g), "Cannot start HTTP servers");
    NOTICE_LOG << "Starting " << HttpServers.size() << " HTTP servers ... OK" << Endl;
}

void TSearchServerBase::Stop()
{
    TGuard<TMutex> g(StateLock);
    if (!IsRunningUnsafe(g)) {
        return ;
    }
    NOTICE_LOG << "Stopping " << HttpServers.size() << " HTTP servers ..." << Endl;
    for (auto &server : HttpServers) {
        server->Stop();
    }
    // THttpServer::Stop() is a sync call, should have 0 running threads by now.
    // If some listener didn't stop then it's better to crash, otherwise server restart may result in a mess.
    VERIFY_WITH_LOG(!IsRunningUnsafe(g), "Cannot stop HTTP servers");
    NOTICE_LOG << "Stopping " << HttpServers.size() << " HTTP servers ... OK" << Endl;
}

void TSearchServerBase::ShutdownAndWait()
{
    TGuard<TMutex> g(StateLock);
    if (!IsRunningUnsafe(g)) {
        return ;
    }
    NOTICE_LOG << "Shutdown " << HttpServers.size() << " HTTP servers ..." << Endl;
    for (auto &server : HttpServers) {
        server->Shutdown();
    }
    NOTICE_LOG << "Shutdown " << HttpServers.size() << " HTTP servers ... OK" << Endl;

    // State may be inconsistent at this point, Shutdown() is an async call. Need to Wait() before checking the state.
    NOTICE_LOG << "Waiting for " << HttpServers.size() << " HTTP servers to stop ..." << Endl;
    for (auto &server : HttpServers) {
        server->Wait();
    }
    NOTICE_LOG << "Waiting for " << HttpServers.size() << " HTTP servers to stop ... OK" << Endl;

    // Now everything should be stopped, can check the state.
    NOTICE_LOG << "Verifying stopped state of " << HttpServers.size() << " HTTP servers ..." << Endl;
    VERIFY_WITH_LOG(!IsRunningUnsafe(g), "HTTP servers are still running");
    NOTICE_LOG << "Verifying stopped state of " << HttpServers.size() << " HTTP servers ... OK" << Endl;
}

bool TSearchServerBase::IsRunning() {
    TGuard<TMutex> g(StateLock);
    return IsRunningUnsafe(g);
}

bool TSearchServerBase::IsRunningUnsafe(const TGuard<TMutex>&) {
    size_t running = (size_t)AtomicGet(RunningCounter);
    if (running != 0 && running != HttpServers.size()) {
        FAIL_LOG("Inconsistent state of TSearchServerBase: (%lu != 0) && (%lu != %lu)", running, running, HttpServers.size());
    }
    return (running != 0);
}

void TSearchServerBase::OnListenStart() {
    VERIFY_WITH_LOG((size_t)AtomicIncrement(RunningCounter) <= HttpServers.size(), "Unexpected RunningCounter value");
}

void TSearchServerBase::OnListenStop() {
    VERIFY_WITH_LOG(AtomicDecrement(RunningCounter) >= 0, "Unexpected negative RunningCounter value");
}

void TSearchServerBase::OnFailRequestEx(const TFailLogData& d)
{
    ERROR_LOG << "Dropped request (failstate " << d.failstate << "): " << d.url << Endl;
}
