#include "client.h"
#include "server.h"
#include "indexer_proxy_specific.h"

#include <saas/library/behaviour/behaviour.h>
#include <saas/indexerproxy/context/action_reply.h>
#include <saas/indexerproxy/logging/rty_ipr.h>
#include <saas/util/logging/tskv_log.h>

#include <library/cpp/http/misc/httpcodes.h>
#include <library/cpp/logger/global/global.h>
#include <library/cpp/protobuf/json/proto2json.h>
#include <library/cpp/string_utils/quote/quote.h>
#include <util/stream/str.h>

static const TString PrefixService = "/service/";

bool THttpProxyIndexerClient::ProcessRequest() {
    TString serviceHash{GetScriptName()};
    try {
        if (strstr(serviceHash.data(), PrefixService.data())) {
            serviceHash = serviceHash.substr(PrefixService.size());
            serviceHash = serviceHash.substr(0, serviceHash.find_first_of('/'));
            TProxyConfig::THashInfoStructure info;
            if (Executor.GetConfig().DecodeServiceHash(serviceHash, info)) {
                ServiceName = info.Service;
                AdapterName = info.Adapter;
            } else {
                ythrow yexception() << "incorrect service hash";
            }
        } else {
            ythrow yexception() << "incorrect uri: " << serviceHash;
        }
    } catch (...) {
        NUtil::TTSKVRecord tskv("saas-ipr-log");
        tskv.AddIsoEventTime().Add("ip", GetRemoteAddr()).Add("url", GetScriptName()).Add("cgi", GetQueryString())
                .ForceAdd("size",GetPost().Length()).Add("status", "fail").Add("http_code", 400)
                .ForceAdd("expl", CurrentExceptionMessage()).Add("kind", "ip-export").ForceAdd("duration", Duration());

        DEBUG_LOG << tskv.ToString() << Endl;
        IPR_LOG << tskv.ToString() << Endl;
        Reply(400, CurrentExceptionMessage(), "");
        return true;
    }

    if (!GetRequestOptions().IsTrace() && GetRequestOptions().IsInstantReply()) {
        TServiceMetricPtr metric = Executor.GetMetrics().Get(GetServiceName());
        CHECK_WITH_LOG(!!metric);
        ui32 limitOnFlight = Executor.GetConfig().GetServicesConfig().GetConfig(GetServiceName()).MaxInFlightForInstant;
        Executor.AddContext(new TNoReplyHttpContext(*this));
        if (metric->GetInFlightCount() > limitOnFlight) {
            TString json = Sprintf("{\"in_flight\":%" PRIu64 ",\"limit_on_flight\":%" PRIu32 "}",
                                   metric->GetInFlightCount(), limitOnFlight);
            NUtil::TTSKVRecord tskv("saas-ipr-log");
            tskv.AddIsoEventTime().Add("ip", GetRemoteAddr()).Add("url", GetScriptName()).Add("cgi", GetQueryString())
                    .ForceAdd("size",GetPost().Length()).Add("status", "limit_on_flying").Add("http_code", 500)
                    .ForceAdd("message", json).Add("kind", "ip-queue").Add("duration", Duration());

            DEBUG_LOG << tskv.ToString() << Endl;
            IPR_LOG << tskv.ToString() << Endl;
            Reply(500, "Max-in-fly documents count limit reached", "NOTNOW");
        } else {
            Reply(200, "", "OK");
        }
        return true;
    } else {
        Executor.AddContext(this);
        return false;
    }
}

void THttpProxyIndexerClient::GetMetrics(IOutputStream& out) const {
    out << "HTTP/1.1 200 Ok\r\n\r\n"sv;
    ::CollectMetrics(out);
}

bool THttpProxyIndexerClient::Reply(void* /*ThreadSpecificResource*/) {
    DEBUG_LOG << "action=parsing_client_data;status=start" << Endl;
    if (!ProcessHeaders()) {
        NoNeedInReply();  //Handler replied already
        return true;
    }
    RD.Scan();
    RequestOptions.Parse(RD);
    if (ProcessSpecialRequest()) {
        DEBUG_LOG << "ip=" << RD.RemoteAddr() << ";url=" << RD.ScriptName() << ";cgi=" << RD.CgiParam.Print() << ";" << Endl;
        return true;
    } else {
        const TString origin{RD.HeaderInOrEmpty("Origin")};
        OriginHeader = !!origin ? ("Access-Control-Allow-Origin:" + origin + "\r\nAccess-Control-Allow-Credentials:true\r\n") : TString();
        return ProcessRequest();
    }
    FAIL_LOG("Undef behavior");
}

THttpProxyIndexerClient::THttpProxyIndexerClient(TTaskExecutor& executor, TFlowMirror& /*flowMirror*/)
    : THttpContext(executor)
{}

bool THttpProxyIndexerClient::ProcessSpecialRequest() {
    if (TBaseHttpClient::ProcessSpecialRequest()) {
        NoNeedInReply();
        return true;
    }

    return false;
}

TServerInfo THttpProxyIndexerClient::GetServerInfo(bool isHumanReadable) const {
    THttpProxyRTYServer::TInfoCollector collector(isHumanReadable);
    SendGlobalMessage(collector);
    TServerInfo result;
    collector.Fill(result);
    return result;
}
