#pragma once

#include <saas/searchproxy/core/searchproxyserver.h>
#include <search/idl/meta.pb.h>

#include <kernel/search_daemon_iface/relevance_type.h>

#include <util/generic/string.h>
#include <util/generic/vector.h>
#include <util/network/socket.h>
#include <library/cpp/http/io/stream.h>
#include <util/string/cast.h>
#include <util/system/defaults.h>
#include <robot/gemini/protos/castor.pb.h>

class TJsonObject;

struct THttpResponse {
    unsigned HttpCode;
    THttpHeaders Headers;
    TString Body;
};

THttpResponse GetHttpData(ui16 port, const char* query);
THttpResponse GetHttpData(ui16 port, const char* query, const THashMap<TString, TString>& headers);
TString GetHeader(const THttpHeaders& headers, const TString& name);

struct TSearchResultsGroup {
    TRelevance Relevance;
    typedef TVector<TString> TUrls;
    TUrls Urls;
    TSearchResultsGroup(TRelevance relevance, const TUrls& urls = TUrls())
        : Relevance(relevance)
        , Urls(urls)
    {
    }
};

struct TSearchResults: TVector<TSearchResultsGroup> {
    const unsigned HttpStatus;
    TSearchResults(unsigned httpStatus)
        : HttpStatus(httpStatus)
    {
    }

    TSearchResults(unsigned httpStatus, const TSearchResultsGroup& group)
        : HttpStatus(httpStatus)
    {
        push_back(group);
    }

    TSearchResults(unsigned httpStatus, const TSearchResultsGroup& group1,
        const TSearchResultsGroup& group2)
        : HttpStatus(httpStatus)
    {
        push_back(group1);
        push_back(group2);
    }
};

class TAutoSearchProxyServer: public TSearchProxyServer {
public:
    TAutoSearchProxyServer(const TSearchProxyConfig& config, const TTestTvmSuite* testSuite = nullptr)
        : TSearchProxyServer(config, testSuite)
    {
        InitGlobalLog2Console(TLOG_INFO);
        Start();
    }
    ~TAutoSearchProxyServer() {
        Stop();
    }
};

TSearchResults ExtractSearchResults(const NMetaProtocol::TReport& report,
    unsigned httpStatus, bool isKv = false);
TSearchResults ExtractSearchResults(const TJsonObject& report, unsigned httpStatus);
NMetaProtocol::TReport GenerateReport(const TSearchResults& data, const bool isKv = false);
NMetaProtocol::TReport GenerateGeminiReport(const TString& url, const TString& mainUrl);
NMetaProtocol::TReport GenerateEmptyReport();

struct TSearchMapLine {
    TString Service;
    ui16 SearchPort;
    ui16 ShardMin;
    ui16 ShardMax;
    TString Group;
    TString ShardBy;
};

inline TSearchMapLine GetSearchMapLine(const char* service, ui16 port, ui16 shardMin = 0,
                                ui16 shardMax = 65533, const char* rank = "master",
                                const char* shardBy = nullptr) {
    Y_UNUSED(rank);
    TSearchMapLine result;
    result.Service = service;
    result.SearchPort = port;
    result.ShardMin = shardMin;
    result.ShardMax = shardMax;
    result.ShardBy = shardBy;
    return result;
}

TString GetSearchMapRule(const char* service, ui16 port, ui16 shardMin = 0,
    ui16 shardMax = 65533, const char* rank = "master",
    const char* shardBy = nullptr);

TString GetSearchMap(const TVector<TSearchMapLine>& lines);

const char* GetDaemonConfig();
TString GetSearchProxyConfig(ui16 port);
TString GetPatientSearchProxyConfig(ui16 port, const char* searchConfig = "", const char* logRoot = nullptr, const char* serviceConfig = nullptr);

TString OneStepRequest(const TString& service, const TString& kps,
    const TString& request, const TString& userRequest = "",
    const TString& how = "rlv");

TString KvRequest(const TString& service, const TString& kps,
    const TString& request);

void CheckResults(const TSearchResults& actual,
    const TSearchResults& expected);
void CheckProtoQuery(ui16 port, const TString& query,
    const TSearchResults& expected, const bool isKV = false);
void CheckJsonQuery(ui16 port, const TString& query,
    const TSearchResults& expected);
void CheckGeminiQuery(ui16 port, const TString& query,
    const NGeminiProtos::TCastorResponse& expected);

const TString DefaultServiceName = "test";
