#include "nel_server.h"

#include <library/cpp/http/server/response.h>
#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/json/json_reader.h>
#include <library/cpp/resource/resource.h>

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


TClientRequest* TNELServer::CreateClient() {
    return new TNELServerRequestReplier(Configuration_, MetricStorage_, Logger_, GeobaseLookup_);
}

bool TNELServerRequestReplier::DoReply(const TReplyParams& replyParams) {
    TParsedHttpFull request(replyParams.Input.FirstLine());

    if (request.Method != "POST") {
        replyParams.Input.ReadAll(Cnull);
        replyParams.Output << THttpResponse(HTTP_NOT_IMPLEMENTED);
        return true;
    }

    TServerRequestData requestData(Socket());
    FillRequestData(requestData, request, replyParams);

    if (!Configuration_.Handles.contains(request.Path) || 
        Configuration_.Handles.at(request.Path).Purpose != EHandlePurpose::NEL
    ) {
        replyParams.Input.ReadAll(Cnull);
        replyParams.Output << THttpResponse(HTTP_NOT_FOUND);
        return true;
    }

    const TString& tenant = Configuration_.Handles.at(request.Path).TenantName;

    TBuffer body;
    if (ReadBody(body, replyParams.Input)) {
        TStringBuf bodyStrBuf(body.Data(), body.Size());

        // Write the request to the accesslog
        Logger_.LogRequest(tenant, bodyStrBuf, requestData);

        // Write NEL reports one by one into metric storage
        NJson::TJsonValue bodyJson;
        NJson::ReadJsonTree(bodyStrBuf, &bodyJson, true);
        const NJson::TJsonValue::TArray& reportsJson = bodyJson.GetArraySafe();

        if (reportsJson.size() > 0) {
            TNELReportSender sender(requestData, GeobaseLookup_);

            for (const NJson::TJsonValue& reportJson : reportsJson) {
                TNELReport report(sender, reportJson);
                Logger_.LogReport(tenant, report);
                MetricStorage_.StoreNELReport(tenant, report);
            }
        }
    } else {
        // TODO
        // MetricStorage_.CountTooLargeRequest();
    }

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

bool TNELServerRequestReplier::ReadBody(TBuffer& body, THttpInput& input) {
    static 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 > MaxBodySize_) {
            input.ReadAll(Cnull);
            return false;
        }
        
        if (bytesRead < readBlockSize) {
            body.Resize(totalBytesRead);
            return true;
        }
    }
}

