#include "common.h"

#include <dict/dictutil/str.h>

#include <saas/library/searchserver/replier.h>
#include <saas/searchproxy/common/cgi.h>
#include <saas/searchproxy/common/headers.h>
#include <saas/searchproxy/search_meta/extendedcontext.h>
#include <search/session/reqenv.h>
#include <search/request/data/reqdata.h>

#include <kernel/search_daemon_iface/cntintrf.h>

#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/string_utils/relaxed_escaper/relaxed_escaper.h>

#include <util/string/cast.h>
#include <util/system/thread.h>
#include <util/system/hostname.h>

using namespace NSearchProxy::NLogging;

inline TString GetClientIpFromRD(const TSearchRequestData& rd) {
    TString result;
    if (!result) {
        result = rd.CgiParam.Get(NSearchProxyCgi::ip);
    }
    if (!result) {
        const auto it = rd.HeadersIn().find(NSearchProxyHeaders::XForwardedForY);
        if (it != rd.HeadersIn().cend())
            result = it->second;
    }
    if (!result) {
        result = rd.RemoteAddr();
    }
    return result;
}

void InsertClientIp(NUtil::TTSKVRecord& record, const TSearchRequestData& rd)
{
    record.Add(ClientIpLable, GetClientIpFromRD(rd));
    record.Add(RawIpLable, rd.RemoteAddr());
}

void InsertIncomingInfo(NUtil::TTSKVRecord& record, const TSearchRequestData& rd)
{
    InsertClientIp(record, rd);
    record.Add(UnixTimestampLable, TInstant::MicroSeconds(rd.RequestBeginTime()).Seconds());
    record.Add(RequestBeginLable, rd.RequestBeginTime());
    record.Add(ThreadIdLable, TThread::CurrentThreadId());
    record.Add(ServerLable, HostName());
    record.Add(QueryLable, rd.Query());
}

void InsertQueryInfo(NUtil::TTSKVRecord& record, const TCgiParameters& cgi, const TSearchRequestData& rd)
{
    record.Add(KpsLable, GetKeyPrefix(cgi));
    record.Add(ServiceLable, cgi.Get(NSearchProxyCgi::service));
    record.Add(UidLable, cgi.Get(NSearchProxyCgi::uid));
    const auto it = rd.HeadersIn().find(NSearchProxyHeaders::XYandexICookie);
    if (it != rd.HeadersIn().cend())
        record.Add(ICookieLable, it->second);

    bool fakeUid = false;
    if (TryFromString<bool>(cgi.Get(NSearchProxyCgi::fake_uid), fakeUid))
        record.Add(FakeUidLable, fakeUid);

    record.Add(UserRequestLable, GetUserRequest(cgi));
    record.Add(QueryIdLable, cgi.Get(NSearchProxyCgi::queryid));
    record.Add(ReqAnsIdLable, cgi.Get(NSearchProxyCgi::raid));
    record.Add(TestInfoLable, cgi.Get(NSearchProxyCgi::test_buckets));
    record.Add(PageLable, cgi.Get(NSearchProxyCgi::p));
    record.Add(GroupingLable, cgi.Get(NSearchProxyCgi::g));
    record.Add(RobotQueryLable, IsTrue(cgi.Get(NSearchProxyCgi::robot)));
    record.Add(RegionLable, cgi.Get(NSearchProxyCgi::region));
    record.Add(ParentReqidLable, cgi.Get(NSearchProxyCgi::parentReqid));
}

TString GetFullCgiParameter(const TCgiParameters& cgi, const TString& name, const char* separator)
{
    const ui64 count = cgi.NumOfValues(name);
    TString result;
    for (ui64 i = 0; i < count; ++i) {
        if (!!result)
            result.append(separator);
        result.append(cgi.Get(name, i));
    }
    return result;
}

ui64 GetRequestDuration(const TSearchRequestData& rd)
{
    return MicroSeconds() - rd.RequestBeginTime();
}

ui64 GetRequestDuration(ISearchContext* searchContext)
{
    return MicroSeconds() - searchContext->ReqEnv()->RequestBeginTime();
}

ui64 GetRequestDuration(TPassageRequestInfo pi)
{
    return MicroSeconds() - pi.Context.StartExecTime();
}

ui64 GetReportDuration(const NSearchProxy::TExtendedContextBase& context) {
    return MicroSeconds() - context.ReportStartTime().MicroSeconds();
}

ui64 GetWaitInQueueDuration(const TExtendedReplyContext& context) {
    return context.ProcessingStartTime().MicroSeconds() - context.GetRequestData().RequestBeginTime();
}

ui64 GetWaitInQueueDuration(const TExtendedSearchContext& context) {
    return context.ProcessingStartTime().MicroSeconds() - context->ReqEnv()->RequestBeginTime();
}

TString EscapeStat(const TStringBuf& s) {
    return NEscJ::EscapeJ<false>(s);
}

TString GetUserRequest(const TCgiParameters& cgi)
{
    const TString& userRequest = cgi.Get("user_request");
    TString result = userRequest.size() ? userRequest : cgi.Get("text");
    ReplaceAnyOf(result, "\t\n\r\0\\", ' ');
    return result;
}

TString GetKeyPrefix(const TCgiParameters& cgi)
{
    if (cgi.Has(NSearchProxyCgi::kps))
        return cgi.Get(NSearchProxyCgi::kps);

    const TString& service = cgi.Get(NSearchProxyCgi::service);
    const TString kpsCgiParam = service + "_kps";
    if (cgi.Has(kpsCgiParam))
        return cgi.Get(kpsCgiParam);

    return "";
}
