#include "logger.h"

#include <library/cpp/json/writer/json.h>
#include <library/cpp/logger/thread.h>
#include <library/cpp/logger/record.h>
#include <library/cpp/string_utils/tskv_format/builder.h>

#include <util/datetime/base.h>
#include <util/stream/str.h>


TLogger::TLogger(
    const TConfiguration& configuration,
    size_t queueSize
)
    : Configuration_(configuration)
{
    if (Configuration_.WriteAccessLog) {
        RequestLog_ = THolder(new TOwningThreadedLogBackend(
            new TFileLogBackend(Configuration_.AccessLogFilePath), 
            queueSize
        ));
    }
    if (Configuration_.WriteReportLog) {
        ReportLog_ = THolder(new TOwningThreadedLogBackend(
            new TFileLogBackend(Configuration_.ReportLogFilePath), 
            queueSize
        ));
    }
}

void TLogger::LogRequest(
    const TString& tenant,
    TStringBuf body,
    const TServerRequestData& reqData
) {
    if (!Configuration_.WriteAccessLog) {
        return;
    }
    Y_ASSERT(RequestLog_);

    NTskvFormat::TLogBuilder builder("neldrlog-request");
    builder.AddUnescaped("timestamp", Now().ToStringLocal())
           .Add("tenant",     tenant)
           .Add("addr",       reqData.HeaderInOrEmpty("X-Forwarded-For-Y"))
           .Add("user-agent", reqData.HeaderInOrEmpty("User-Agent"))
           .Add("content",    body)
           .Add("path",       reqData.ScriptName())
           .End();
    const TString& logLine = builder.Str();
    try {
        RequestLog_->WriteData(TLogRecord(TLOG_INFO, logLine.Data(), logLine.Size()));
    } catch (...) {
        Cerr << Now().ToStringLocal() << ' '
             << "Couldn't write an access log line.";
    }
}

void TLogger::LogReport(
    const TString& tenant,
    TNELReport& report
) {
    if (!Configuration_.WriteReportLog) {
        return;
    }
    Y_ASSERT(ReportLog_);

    NJsonWriter::TBuf json;
    json.BeginObject();

    // Timestamp.
    json.WriteKey("timestamp").WriteString(Now().ToStringLocal());

    // Fields regarding the neldrlog server and it's configuration.
    json.WriteKey("tenant")       .WriteString(tenant);
    json.WriteKey("neldrlog-path").WriteString(report.Sender.NeldrlogPath);

    // Fields extracted from the NEL report.
    json.WriteKey("report-host")       .WriteString(report.Url.GetHost());
    json.WriteKey("report-path")       .WriteString(report.Url.GetField(NUri::TField::FieldPath));
    json.WriteKey("report-ip")         .WriteString(report.ServerIP);
    json.WriteKey("report-status-code").WriteInt(report.StatusCode);
    json.WriteKey("nel-error-type")    .WriteString(report.NELErrorType);
    json.WriteKey("error-type-group")  .WriteString(report.GroupedErrorType);

    // Fields regarding the NEL report sender.
    json.WriteKey("sender-ip")          .WriteString(report.Sender.IP);
    json.WriteKey("sender-user-agent")  .WriteString(report.Sender.UserAgent);
    json.WriteKey("sender-os-family")   .WriteString(report.Sender.OSFamily);
    json.WriteKey("sender-browser-name").WriteString(report.Sender.BrowserName);
    json.WriteKey("sender-is-mobile")   .WriteBool(report.Sender.IsMobile);
    json.WriteKey("sender-asns")        .BeginList();
    for (const TString& asn : report.Sender.ASNs) {
        json.WriteString(asn);
    }
    json.EndList();

    json.EndObject();

    const TString& logLine = json.Str() + "\n";
    try {
        ReportLog_->WriteData(TLogRecord(TLOG_INFO, logLine.Data(), logLine.Size()));
    } catch (...) {
        Cerr << Now().ToStringLocal() << ' '
             << "Couldn't write a report log line.";
    }
}

void TLogger::ReopenLog() {
    if (Configuration_.WriteAccessLog) {
        Y_ASSERT(RequestLog_);
        RequestLog_->ReopenLog();
    }
    if (Configuration_.WriteReportLog) {
        Y_ASSERT(ReportLog_);
        ReportLog_->ReopenLog();
    }
}

