#include "stats_server.h"

#include <library/cpp/monlib/encode/spack/spack_v1_encoder.cpp>
#include <library/cpp/http/server/response.h>
#include <library/cpp/cgiparam/cgiparam.h>
#include <library/cpp/http/misc/parsed_request.h>
#include <library/cpp/json/json_writer.h>

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

TClientRequest* TStatsServer::CreateClient() {
    return new TStatsRequestReplier(this);
}

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

bool TStatsRequestReplier::DoReply(const TReplyParams& params) {
    TParsedHttpFull req(params.Input.FirstLine());
    params.Input.ReadAll(Cnull);
    if (req.Method != "GET") {
        params.Output << THttpResponse(HTTP_NOT_IMPLEMENTED);
        return true;
    }
    if (req.Path == "/unistat") {
        WriteYasmStats(params.Output);
    } else if (req.Path == "/solomon") {
        // TODO: parse duration from cgi and validate its value
        TCgiParameters cgi(req.Cgi);
        WriteSolomonStats(
            params.Output,
            TInstant::ParseIso8601(cgi.Get("now")),
            TDuration::Seconds(15)
        );
    } else {
        params.Output << THttpResponse(HTTP_NOT_FOUND);
    }
    return true;
}

void TStatsRequestReplier::WriteYasmStats(THttpOutput& out) {
    out << "HTTP/1.1 200 Ok\r\n\r\n";
    NJson::TJsonWriter jsonWriter(&out, false);
    jsonWriter.OpenArray();
    for (const auto& [signal, atomic] : Server_->YasmStats->Stats()) {
        jsonWriter.OpenArray();
        jsonWriter.Write(signal);
        jsonWriter.Write(AtomicGet(atomic));
        jsonWriter.CloseArray();
    }
    jsonWriter.CloseArray();
    jsonWriter.Flush();
}

void TStatsRequestReplier::WriteSolomonStats(THttpOutput& out, TInstant solomonNow, TDuration solomonPeriod) {
    out << "HTTP/1.1 200 Ok\r\n";
    out << "Content-Type: application/x-solomon-spack\r\n\r\n";
    auto& stats = *Server_->SolomonStats;
    auto timeToRead = solomonNow - stats.StatsBuffer().MaxWriteAge;
    if (stats.Interval() != solomonPeriod) {
        out << THttpResponse(HTTP_BAD_REQUEST).SetContent("unsupported period value");
        return;
    }
    auto encoder = NMonitoring::EncoderSpackV1(&out,
        NMonitoring::ETimePrecision::SECONDS,
        NMonitoring::ECompression::LZ4);
    encoder->OnStreamBegin();
    {
        encoder->OnCommonTime(timeToRead);
    }
    {
        encoder->OnLabelsBegin();
        encoder->OnLabel("project", "drlog");
        encoder->OnLabelsEnd();
    }
    {
        auto slices = stats.SlicesConfig().Slices;
        stats.StatsBuffer().VisitReadableStats(solomonNow, timeToRead,
            [&encoder, &slices](const TStatsBuffer::TStats& stats) {

            for (auto slice = slices.cbegin(); slice != slices.cend(); ++slice) {
                encoder->OnMetricBegin(NMonitoring::EMetricType::IGAUGE);
                {
                    encoder->OnLabelsBegin();
                    encoder->OnLabel("queried_host", slice->HostSubslice);
                    encoder->OnLabel("status", slice->Status);
                    encoder->OnLabel("asn", slice->ASN);
                    encoder->OnLabelsEnd();
                }
                size_t idx = slice - slices.cbegin();
                encoder->OnUint64(TInstant::Zero(), stats[idx]);
                encoder->OnMetricEnd();
            }
        });
    }
    encoder->OnStreamEnd();
    encoder->Close();
}
