#include "converter.h"
#include "json.h"
#include "suggest.h"
#include "minimal.h"

#include <saas/library/report_builder/fake.h>

#include <search/request/treatcgi/treatcgi.h>
#include <search/session/searcherprops.h>

#include <library/cpp/testing/unittest/registar.h>
#include <library/cpp/logger/global/global.h>

#include <util/generic/algorithm.h>
#include <util/generic/hash.h>

class TTestReportContext : public TFakeReportContext {
public:
    TTestReportContext(const TCgiParameters& params)
        : TFakeReportContext(params)
    {
        TreatCgiParams(RP, params);
    }

    const TRequestParams& GetRP() const override {
        return RP;
    }

    long GetRequestedPage() const override {
        return RP.RequestedPage;
    }

    void AddReplyInfo(const TString& key, const TString& value) final {
        Headers[key] = value;
    }

    const THashMap<TString, TString>& GetHeaders() const {
        return Headers;
    }

private:
    TRequestParams RP;
    THashMap<TString, TString> Headers;
};

class TTestReplyData : public ISearchReplyData {
public:
    TTestReplyData(ui64 docsCount)
        : GroupDocsCount({docsCount})
    {}

    TTestReplyData(const TVector<ui64>& groupDocsCount)
        : GroupDocsCount(groupDocsCount)
    {}

    void FillArchiveInfo(int nGroup, int nDoc, const TGroupingIndex& /*gi*/, bool passages, TMainDocArchiveInfo& info) const override {
        info.UrlProperty = "some_url_" + ToString(nDoc);
        info.Url = "some_url_" + ToString(nDoc);
        info.DocId = TDocHandle(nDoc, TDocRoute({ui16(nGroup)}));
        info.Relevance = 10000;

        info.HeadLine = Headline;
        info.Title = Title;

        if (passages) {
            for (const auto& passage: OwnPassages) {
                info.Passages.push_back(passage.c_str());
            }
        }
    }

    void FillReportStat(TFullReportInfo& info) const override {
        const ui64 totalDocs = Accumulate(GroupDocsCount.cbegin(), GroupDocsCount.cend(), 0);
        info.Stat[0] = totalDocs;
        info.Stat[1] = totalDocs;
        info.Stat[2] = totalDocs;
    }

    void FillGroupingInfo(const TGroupingIndex& /*gi*/, TGroupingInfo& info) const override {
        const ui64 totalDocs = Accumulate(GroupDocsCount.cbegin(), GroupDocsCount.cend(), 0);
        info.Stat[0] = totalDocs;
        info.Stat[1] = totalDocs;
        info.Stat[2] = totalDocs;
        info.GroupsCount = 1;
    }

    void FillGroupInfo(int nGroup, const TGroupingIndex& /*gi*/, TGroupInfo& info) const override {
        const ui64 groupDocsCount = GroupDocsCount.at(nGroup);
        info.Stat[0] = groupDocsCount;
        info.Stat[1] = groupDocsCount;
        info.Stat[2] = groupDocsCount;
        info.Relevance = 100;
        info.DocsCount =  groupDocsCount;
        info.CategString = "test_categ_" + ToString(nGroup);
    }

    void FillDocCategInfo(int /*group*/, int /*doc*/, const TGroupingIndex& /*gi*/, const TString& /*attrName*/, int /*docInCateg*/, TDocCategInfo& /*info*/) const override {
    }

    void FillIndexAttrsInfo(int /*group*/, int /*doc*/, const TGroupingIndex& /*gi*/, TAttrsInfo& /*info*/) const override {
    }

    void SerializeAttributes(int nGroup, int nDoc, const TGroupingIndex& /*gi*/, TArrayRef<const char* const> /*attrName*/, IAttributeWriter& writer, bool /*withSafe*/) const override {
        writer("test_key", (TString("attr_val_") + ToString(nGroup) + "_" + ToString(nDoc)).data());
        writer("test_key_int", (ToString(nDoc)).data());
        writer("test_key_json", (TString("{\"attr_val_") + ToString(nGroup) + "_" + ToString(nDoc) + "\": \"value\"}").data());
        writer("test_key_dup", "d1");
        writer("test_key_dup", "d2");
    }

    TSearcherPropsRef GetSearchProperties() const override {
        TSearcherPropsRef props = MakeIntrusive<TSearcherProps>();
        (*props)["TestProp"] = "PropVal";
        return props;
    }

    void FillErrors(TVector<TString>& /*errors*/) const override {
    }

    void FillPagingInfo(const TGroupingIndex& /*gi*/, const int groups,
        const long int requestedPage, TPagingInfo& paging) const override
    {
        paging.FirstGroup = requestedPage * groups;
        paging.LastGroup = paging.FirstGroup + groups;
        if (paging.LastGroup > GroupDocsCount.size()) {
            paging.LastGroup = GroupDocsCount.size();
        }
    }

    void AddPassage(const TString& passage) {
        OwnPassages.push_back(passage);
    }

    void SetTitle(const TString& title) {
        Title = title;
    }

    void SetHeadline(const TString& headline) {
        Headline = headline;
    }

private:
    TVector<ui64> GroupDocsCount = {1};
    TVector<TString> OwnPassages;
    TString Title = "some_title";
    TString Headline  = "some_headLine";
};


Y_UNIT_TEST_SUITE(ReportFormatterTests) {

    TCgiParameters CreateCgi(const TString& requestStr) {
        TCgiParameters cgi;
        cgi.Scan(requestStr);
        return cgi;
    }

    TString GetReportString(const TReportConstructor& constructor, IReportFormatter& formatter) {
        TBuffer report = constructor.BuildReport(formatter);
        TString result(report.data(), report.size());
        INFO_LOG << result << Endl;
        return result;
    }

    NJson::TJsonValue GetReportJson(const TReportConstructor& constructor, IReportFormatter& formatter) {
        TString result = GetReportString(constructor, formatter);
        NJson::TJsonValue json;
        UNIT_ASSERT(NJson::ReadJsonFastTree(result, &json));
        return json;
    }

    template<typename T>
    NJson::TJsonValue GenerateJsonReport(const TString& cgiStr, const TTestReplyData& replyData) {
        TCgiParameters cgi = CreateCgi(cgiStr);

        TTestReportContext context(cgi);
        TReportConstructor constructor(replyData, context);
        T formatter(cgi);
        return GetReportJson(constructor, formatter);
    }

    Y_UNIT_TEST(MinimalJsonReport) {
        TTestReplyData replyData({2, 3});
        NJson::TJsonValue json  = GenerateJsonReport<TJsonFormatter>("&text=test", replyData);

        UNIT_ASSERT_VALUES_EQUAL(json["page"], "0");

        UNIT_ASSERT(json.Has("groupings"));
        UNIT_ASSERT(json["groupings"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"].GetArray().size(), 1);
        UNIT_ASSERT(json["groupings"][0].IsMap());
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["categ"], "");
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["attr"], "");
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["mode"], "flat");

        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["all"], "5");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["strict"], "5");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["phrase"], "5");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["searcher_properties"]["TestProp"], "PropVal");
        UNIT_ASSERT(json["response"]["results"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"].GetArray().size(), 1);
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups-on-page"], "10");

        UNIT_ASSERT(json["response"]["results"][0]["groups"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"].GetArray().size(), 2);
        UNIT_ASSERT(json["response"]["results"][0]["groups"][0]["documents"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"].GetArray().size(), 2);

        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][0]["url"], "some_url_0");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["url"], "some_url_1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][0]["properties"]["test_key"], "attr_val_0_0");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["properties"]["test_key"], "attr_val_0_1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["properties"]["test_key_dup"][0], "d1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["properties"]["test_key_dup"][1], "d2");

        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"].GetArray().size(), 3);
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][0]["url"], "some_url_0");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][1]["url"], "some_url_1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][0]["properties"]["test_key"], "attr_val_1_0");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][1]["properties"]["test_key"], "attr_val_1_1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][2]["properties"]["test_key"], "attr_val_1_2");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][2]["properties"]["test_key_dup"][0], "d1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][2]["properties"]["test_key_dup"][1], "d2");
    }

    Y_UNIT_TEST(JsonReportGroupingStats) {
        const TTestReplyData replyData({2, 3, 3, 3, 1});

        NJson::TJsonValue json = GenerateJsonReport<TJsonFormatter>("&text=test&g=1.s_int_chat_id.1.1.1.1.1.1.s_message_history_id.0.count",
            replyData);

        UNIT_ASSERT_VALUES_EQUAL(json["page"], "0");

        UNIT_ASSERT(json.Has("groupings"));
        UNIT_ASSERT(json["groupings"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"].GetArray().size(), 1);
        UNIT_ASSERT(json["groupings"][0].IsMap());
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["categ"], "1");
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["attr"], "s_int_chat_id");
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["mode"], "deep");
        UNIT_ASSERT_VALUES_EQUAL(json["groupings"][0]["groups-on-page"], "1");
    }

    Y_UNIT_TEST(JsonReportPaging) {
        const TTestReplyData replyData({2, 3, 3, 3, 1});

        {
            NJson::TJsonValue json = GenerateJsonReport<TJsonFormatter>("&text=test&numdoc=3&p=0", replyData);

            UNIT_ASSERT_VALUES_EQUAL(json["page"], "0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["all"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["strict"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["phrase"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"].GetArray().size(), 1);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"].GetArray().size(), 3);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"].GetArray().size(), 2);

            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][0]["properties"]["test_key"], "attr_val_0_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["properties"]["test_key"], "attr_val_0_1");

            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"].GetArray().size(), 3);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][0]["properties"]["test_key"], "attr_val_1_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][1]["properties"]["test_key"], "attr_val_1_1");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][2]["properties"]["test_key"], "attr_val_1_2");

            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][2]["documents"].GetArray().size(), 3);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][2]["documents"][0]["properties"]["test_key"], "attr_val_2_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][2]["documents"][1]["properties"]["test_key"], "attr_val_2_1");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][2]["documents"][2]["properties"]["test_key"], "attr_val_2_2");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TJsonFormatter>("&text=test&numdoc=3&p=1", replyData);

            UNIT_ASSERT_VALUES_EQUAL(json["page"], "1");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["all"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["strict"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["phrase"], "12");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"].GetArray().size(), 1);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"].GetArray().size(), 2);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"].GetArray().size(), 3);

            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][0]["url"], "some_url_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["url"], "some_url_1");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][2]["url"], "some_url_2");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][0]["properties"]["test_key"], "attr_val_3_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][1]["properties"]["test_key"], "attr_val_3_1");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"][2]["properties"]["test_key"], "attr_val_3_2");

            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"].GetArray().size(), 1);
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][0]["url"], "some_url_0");
            UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][1]["documents"][0]["properties"]["test_key"], "attr_val_4_0");
        }
    }

    void CheckHilightedEntry(const NJson::TJsonValue& entry, const TString& text, const TMaybe<TString>& priority = Nothing()) {
        UNIT_ASSERT(entry.IsMap());
        if (priority.Defined()) {
            UNIT_ASSERT_VALUES_EQUAL(entry.GetMap().size(), 2);
            UNIT_ASSERT_VALUES_EQUAL(entry["priority"], priority.GetRef());
        } else {
            UNIT_ASSERT_VALUES_EQUAL(entry.GetMap().size(), 1);
        }
        UNIT_ASSERT_VALUES_EQUAL(entry["text"], text);
    }

    Y_UNIT_TEST(JsonHighlight) {
        TTestReplyData replyData(1);
        replyData.AddPassage("Passage with \007(all\007) priority \007(highlight\007)");
        replyData.AddPassage("Passage with \007[strict\007] priority \007[highlight\007]");
        replyData.SetTitle("Passage with \007{phrase\007} priority");
        replyData.SetHeadline("Passage with \007{phrase\007} priority");

        NJson::TJsonValue json  = GenerateJsonReport<TJsonFormatter>("&text=test&snip=in_json_report%3Dyes", replyData);

        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["all"], "1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["strict"], "1");
        UNIT_ASSERT(json["response"]["results"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"].GetArray().size(), 1);
        UNIT_ASSERT(json["response"]["results"][0]["groups"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"].GetArray().size(), 1);
        UNIT_ASSERT(json["response"]["results"][0]["groups"][0]["documents"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"].GetArray().size(), 1);

        const auto& document = json["response"]["results"][0]["groups"][0]["documents"][0];
        {
            const auto& passages = document["passages"];
            UNIT_ASSERT(passages.IsArray());
            UNIT_ASSERT_VALUES_EQUAL(passages.GetArray().size(), 2);


            UNIT_ASSERT(passages[0].IsArray());
            UNIT_ASSERT_VALUES_EQUAL(passages[0].GetArray().size(), 4);
            CheckHilightedEntry(passages[0][0], "Passage with ");
            CheckHilightedEntry(passages[0][1], "all", TMaybe<TString>("all"));
            CheckHilightedEntry(passages[0][2], " priority ");
            CheckHilightedEntry(passages[0][3], "highlight", TMaybe<TString>("all"));

            UNIT_ASSERT(passages[1].IsArray());
            UNIT_ASSERT_VALUES_EQUAL(passages[1].GetArray().size(), 4);
            CheckHilightedEntry(passages[1][0], "Passage with ");
            CheckHilightedEntry(passages[1][1], "strict", TMaybe<TString>("strict"));
            CheckHilightedEntry(passages[1][2], " priority ");
            CheckHilightedEntry(passages[1][3], "highlight", TMaybe<TString>("strict"));
        }
        {
            const auto& title = document["title"];
            UNIT_ASSERT(title.IsArray());
            UNIT_ASSERT_VALUES_EQUAL(title.GetArray().size(), 3);
            CheckHilightedEntry(title[0], "Passage with ");
            CheckHilightedEntry(title[1], "phrase", TMaybe<TString>("phrase"));
            CheckHilightedEntry(title[2], " priority");
        }
        {
            const auto& headline = document["headline"];
            UNIT_ASSERT(headline.IsArray());
            UNIT_ASSERT_VALUES_EQUAL(headline.GetArray().size(), 3);
            CheckHilightedEntry(headline[0], "Passage with ");
            CheckHilightedEntry(headline[1], "phrase", TMaybe<TString>("phrase"));
            CheckHilightedEntry(headline[2], " priority");
        }
    }

    Y_UNIT_TEST(JsonEmptyTitleHeadline) {
        TTestReplyData replyData(1);
        replyData.SetHeadline("");
        replyData.SetTitle("");
        NJson::TJsonValue json  = GenerateJsonReport<TJsonFormatter>("&text=test&snip=in_json_report%3Dyes", replyData);

        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["all"], "1");
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["found"]["strict"], "1");
        UNIT_ASSERT(json["response"]["results"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"].GetArray().size(), 1);
        UNIT_ASSERT(json["response"]["results"][0]["groups"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"].GetArray().size(), 1);
        UNIT_ASSERT(json["response"]["results"][0]["groups"][0]["documents"].IsArray());
        UNIT_ASSERT_VALUES_EQUAL(json["response"]["results"][0]["groups"][0]["documents"].GetArray().size(), 1);

        const auto& document = json["response"]["results"][0]["groups"][0]["documents"][0];
        UNIT_ASSERT(!document.Has("headline"));
        UNIT_ASSERT(!document.Has("title"));
    }

    Y_UNIT_TEST(MinimalReport) {
        {
            TCgiParameters cgi = CreateCgi("&text=test&gta=test_key");

            TTestReportContext context(cgi);
            TTestReplyData replyData(2);
            TReportConstructor constructor(replyData, context);
            TMinimalFormatter formatter(cgi);

            TString result = GetReportString(constructor, formatter);
            UNIT_ASSERT_VALUES_EQUAL(result, "attr_val_0_0");
        }
        {
            TCgiParameters cgi = CreateCgi("&text=test&gta=test_unknown_key");

            TTestReportContext context(cgi);
            TTestReplyData replyData(2);
            TReportConstructor constructor(replyData, context);
            TMinimalFormatter formatter(cgi);

            TString result = GetReportString(constructor, formatter);
            UNIT_ASSERT_VALUES_EQUAL(result, "");
        }
    }

    template<typename T>
    std::pair<TString, THashMap<TString, TString>> GenerateReportStringWithHeaders(const TString& cgiStr, const TTestReplyData& data) {
        TCgiParameters cgi = CreateCgi(cgiStr);
        TTestReportContext context(cgi);
        TReportConstructor constructor(data, context);
        T formatter(cgi);
        formatter.FillHeaders(context);
        return std::make_pair(GetReportString(constructor, formatter), context.GetHeaders());

    }

    Y_UNIT_TEST(JsonEmptyTitleHeadlineJsonp) {
        TTestReplyData replyData(1);
        replyData.SetHeadline("");
        replyData.SetTitle("");

        std::pair<TString, THashMap<TString, TString>> plainReportData = GenerateReportStringWithHeaders<TJsonFormatter>("&text=test", replyData);
        UNIT_ASSERT_VALUES_EQUAL(plainReportData.second["Content-Type"], "application/json; charset=utf-8");
        std::pair<TString, THashMap<TString, TString>> jsonpReportData = GenerateReportStringWithHeaders<TJsonFormatter>("&text=test&callback=func", replyData);

        UNIT_ASSERT_VALUES_EQUAL(jsonpReportData.first, "func(" + plainReportData.first + ");");
        UNIT_ASSERT_VALUES_EQUAL(jsonpReportData.second["Content-Type"], "application/javascript; charset=utf-8");
    }

    Y_UNIT_TEST(YSuggestReportJsonP) {
        const TTestReplyData replyData(1);
        std::pair<TString, THashMap<TString, TString>> plainReportData = GenerateReportStringWithHeaders<TYSuggestFormatter>("&text=test", replyData);
        UNIT_ASSERT_VALUES_EQUAL(plainReportData.second["Content-Type"], "application/json; charset=utf-8");
        std::pair<TString, THashMap<TString, TString>> jsonpReportData = GenerateReportStringWithHeaders<TYSuggestFormatter>("&text=test&callback=func", replyData);

        UNIT_ASSERT_VALUES_EQUAL(jsonpReportData.first, "func(" + plainReportData.first + ");");
        UNIT_ASSERT_VALUES_EQUAL(jsonpReportData.second["Content-Type"], "application/javascript; charset=utf-8");
    }

    Y_UNIT_TEST(YSuggestReportSingleDoc) {
        const TTestReplyData replyData(1);
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test", replyData);

            UNIT_ASSERT_VALUES_EQUAL(json[0], "test");
            UNIT_ASSERT_VALUES_EQUAL(json[1][0], "some_url_0");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_report_field=test_key", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "test");
            UNIT_ASSERT_VALUES_EQUAL(json[1][0], "attr_val_0_0");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "some_url_0");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=_Title", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "some_title");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "attr_val_0_0");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key_int&ysuggest_as_string=false", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], 0);
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key_json&ysuggest_as_string=false", replyData);
            UNIT_ASSERT(json[0].IsMap());
        }
    }

    Y_UNIT_TEST(YSuggestReportMultiDoc) {
        const TTestReplyData replyData(2);
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test", replyData);

            UNIT_ASSERT_VALUES_EQUAL(json[0], "test");
            UNIT_ASSERT_VALUES_EQUAL(json[1][0], "some_url_0");
            UNIT_ASSERT_VALUES_EQUAL(json[1][1], "some_url_1");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_report_field=test_key", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "test");
            UNIT_ASSERT_VALUES_EQUAL(json[1][0], "attr_val_0_0");
            UNIT_ASSERT_VALUES_EQUAL(json[1][1], "attr_val_0_1");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "some_url_0");
            UNIT_ASSERT_VALUES_EQUAL(json[1], "some_url_1");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=_Title", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "some_title");
            UNIT_ASSERT_VALUES_EQUAL(json[1], "some_title");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], "attr_val_0_0");
            UNIT_ASSERT_VALUES_EQUAL(json[1], "attr_val_0_1");
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key_int&ysuggest_as_string=false", replyData);
            UNIT_ASSERT_VALUES_EQUAL(json[0], 0);
            UNIT_ASSERT_VALUES_EQUAL(json[1], 1);
        }
        {
            NJson::TJsonValue json = GenerateJsonReport<TYSuggestFormatter>("&text=test&ysuggest_responses_only=yes&ysuggest_report_field=test_key_json&ysuggest_as_string=false", replyData);
            UNIT_ASSERT(json[0].IsMap());
            UNIT_ASSERT(json[1].IsMap());
        }
    }
}
