#include "collector_server.h"

#include <library/cpp/http/server/response.h>
#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/http/misc/parsed_request.h>

#include <util/stream/null.h>
#include <util/stream/file.h>

TClientRequest* TCollectorServer::CreateClient() {
    return new TCollectorRequestReplier(this);
}

void TCollectorServer::OnException() {
    // TODO: write into an errorlog with timestamps
    Cerr << CurrentExceptionMessage() << "\n";
}

bool TCollectorRequestReplier::DoReply(const TReplyParams& params) {
    TParsedHttpFull req(params.Input.FirstLine());
    if (req.Method == "GET") {
        params.Input.ReadAll(Cnull);
        params.Output << THttpResponse(HTTP_NOT_FOUND);
        return true;
    } else if (req.Method != "POST") {
        params.Input.ReadAll(Cnull);
        params.Output << THttpResponse(HTTP_NOT_IMPLEMENTED);
        return true;
    }

    TServerRequestData reqData(Socket());
    if (!reqData.Parse(TString(req.Request).data())) {
        ythrow yexception() << "couldn't parse the Request-Line";
    }
    reqData.Scan();
    for (auto& h : params.Input.Headers()) {
        reqData.AddHeader(h.Name(), h.Value());
    }

    if (ReadBody(params.Input)) {
        TStringBuf bodyStrBuf(Body_.Data(), Body_.Size());

        if (req.Path == "/tcpinfo") {
            Server_->StatsCollector->CollectRequestTcpInfo(bodyStrBuf, reqData);
        } else {
            Server_->LogsCollector->CollectRequest(bodyStrBuf, reqData);
            Server_->StatsCollector->CollectRequest(bodyStrBuf, reqData);
        }
    } else {
        // TODO: show too-large requests in self-stat
    }

    params.Output << THttpResponse(HTTP_OK);
    return true;
}

bool TCollectorRequestReplier::ReadBody(THttpInput& input) {
    const size_t readBlockSize = 4 * 1024;
    size_t bytesRead, totalBytesRead = 0;
    while (true) {
        Body_.Resize(totalBytesRead + readBlockSize);
        bytesRead = input.Load(Body_.Data() + totalBytesRead, readBlockSize);
        totalBytesRead += bytesRead;
        if (totalBytesRead > MaxInputSize_) {
            input.ReadAll(Cnull);
            return false;
        }
        if (bytesRead < readBlockSize) {
            Body_.Resize(totalBytesRead);
            return true;
        }
    }
}
