#include "helpers.h"
#include "staticserver.h"

#include <saas/searchproxy/configs/searchproxyconfig.h>
#include <saas/searchproxy/core/searchproxyserver.h>

#include <saas/library/daemon_base/config/daemon_config.h>
#include <saas/library/searchmap/searchmap.h>
#include <saas/library/searchmap/parsers/json/json.h>
#include <search/idl/meta.pb.h>

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

#include <util/datetime/base.h>
#include <util/generic/string.h>
#include <util/stream/str.h>
#include <util/string/cast.h>
#include <util/string/subst.h>
#include <util/system/defaults.h>
#include <util/system/maxlen.h>

using NUnitTest::RandomString;

class TMisspellTest: public TTestBase {
private:
    UNIT_TEST_SUITE(TMisspellTest)
        UNIT_TEST(TestTemplateSubstitution)
        UNIT_TEST(TestErratum)
        UNIT_TEST(TestErratumRevert)
    UNIT_TEST_SUITE_END();

public:
    void TestTemplateSubstitution() {
        TStaticServer staticServer;
        const TSearchResults data(200, TSearchResultsGroup(100500,
            TSearchResultsGroup::TUrls(1, RandomString(URL_MAX / 2, 1).Quote())));
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533",
            "text%3A%28some+text%29", "some+text").data(),
            GenerateReport(data).SerializeAsString()));
        TString searchMapTxt(GetSearchMapRule("test", staticServer.GetPort()));
        TStringInput searchMap(searchMapTxt);
        NSearchMapParser::TJsonSearchMapParser parser(searchMap);
        // Temporary static server will find free port, then occupy and
        // immediately release it.
        const ui16 port = TSilentServer().GetPort();
        TConfigPatcher cp;
        TSearchProxyConfig config(
            GetPatientSearchProxyConfig(port).data(),
            TDaemonConfig(GetDaemonConfig(), true), parser.GetSearchMap(), cp);
        TAutoSearchProxyServer searchProxy(config);
        CheckProtoQuery(port,
            "/?service=test&template=text:(%request%)&text=some+text&kps=1%2C65533",
            data);
    }

    void TestErratum() {
        TStaticServer staticServer;
        const TSearchResults dataNoResults(200);
        const TSearchResults dataOriginal(200, TSearchResultsGroup(100500,
            TSearchResultsGroup::TUrls(1, "original.url")));
        const TSearchResults dataMisspelled(200, TSearchResultsGroup(100500,
            TSearchResultsGroup::TUrls(1, "misspelled.url")));
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533", "noresultswhatsoever").data(),
            GenerateReport(dataNoResults).SerializeAsString()));
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533", "vfcrdf").data(),
            GenerateReport(dataOriginal).SerializeAsString()));
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533",
            "%D0%BC%D0%BE%D1%81%D0%BA%D0%B2%D0%B0").data(),
            GenerateReport(dataMisspelled).SerializeAsString()));
        TStaticServer erratum;
        UNIT_ASSERT(erratum.AddDoc("/misspell/check?fix=1&srv=saas&text=vfcrdf",
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
            "<MisspellResult code=\"201\" lang=\"ru,en\" rule=\"KeyboardLayout\" flags=\"1024\" r=\"10000\"><srcText>vfcrdf</srcText><text>москва</text></MisspellResult>"));
        UNIT_ASSERT(erratum.AddDoc("/misspell/check?fix=1&srv=saas&text=noresultswhatsoever",
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
            "<MisspellResult code=\"201\" lang=\"ru,en\" rule=\"KeyboardLayout\" flags=\"1024\" r=\"10000\"><srcText>noresultswhatsoever</srcText><text>москва</text></MisspellResult>"));
        TString searchMapTxt(GetSearchMapRule("test", staticServer.GetPort()));
        TStringInput searchMap(searchMapTxt);
        NSearchMapParser::TJsonSearchMapParser parser(searchMap);
        // Temporary static server will find free port, then occupy and
        // immediately release it.
        const ui16 port = TSilentServer().GetPort();
        TConfigPatcher cp;
        TSearchProxyConfig config(
            GetPatientSearchProxyConfig(port,
            ("<Misspell>\n"
            "\tHost: localhost\n"
            "\tPort: " + ToString(erratum.GetPort()) + "\n"
            "</Misspell>\n").data()).data(),
            TDaemonConfig(GetDaemonConfig(), true), parser.GetSearchMap(), cp);

        TAutoSearchProxyServer searchProxy(config);
        CheckProtoQuery(port, "/?service=test&text=vfcrdf&kps=1%2C65533", dataMisspelled);
        CheckProtoQuery(port, "/?service=test&text=vfcrdf&kps=1%2C65533&msp=force", dataMisspelled);
        CheckProtoQuery(port, "/?service=test&text=vfcrdf&kps=1%2C65533&msp=no", dataOriginal);
        CheckProtoQuery(port, "/?service=test&text=vfcrdf&kps=1%2C65533&msp=try_at_first", dataOriginal);

        CheckProtoQuery(port, "/?service=test&text=noresultswhatsoever&kps=1%2C65533", dataMisspelled);
        CheckProtoQuery(port, "/?service=test&text=noresultswhatsoever&kps=1%2C65533&msp=force", dataMisspelled);
        CheckProtoQuery(port, "/?service=test&text=noresultswhatsoever&kps=1%2C65533&msp=no", TSearchResults(404));
        CheckProtoQuery(port, "/?service=test&text=noresultswhatsoever&kps=1%2C65533&msp=try_at_first", dataMisspelled);
    }

    void TestErratumRevert() {
        TStaticServer staticServer;
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533", "vfcrdf").data(),
            GenerateReport(TSearchResults(200)).SerializeAsString()));
        UNIT_ASSERT(staticServer.AddDoc(OneStepRequest(DefaultServiceName, "1%2C65533",
            "%D0%BC%D0%BE%D1%81%D0%BA%D0%B2%D0%B0").data(),
            GenerateReport(TSearchResults(200)).SerializeAsString()));
        TStaticServer erratum;
        UNIT_ASSERT(erratum.AddDoc("/misspell/check?&text=vfcrdf&fix=1",
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
            "<MisspellResult code=\"201\" lang=\"ru,en\" rule=\"KeyboardLayout\" flags=\"1024\" r=\"10000\"><srcText>vfcrdf</srcText><text>москва</text></MisspellResult>"));
        TString searchMapTxt(GetSearchMapRule("test", staticServer.GetPort()));
        TStringInput searchMap(searchMapTxt);
        NSearchMapParser::TJsonSearchMapParser parser(searchMap);
        // Temporary static server will find free port, then occupy and
        // immediately release it.
        const ui16 port = TSilentServer().GetPort();
        TConfigPatcher cp;
        TSearchProxyConfig config(
            GetPatientSearchProxyConfig(port,
            ("<Misspell>\n"
            "\tHost: localhost\n"
            "\tPort: " + ToString(erratum.GetPort()) + "\n"
            "</Misspell>\n").data()).data(),
            TDaemonConfig(GetDaemonConfig(), true), parser.GetSearchMap(), cp);
        TAutoSearchProxyServer searchProxy(config);
        CheckProtoQuery(port, "/?service=test&text=vfcrdf&kps=1%2C65533",
            TSearchResults(404));
    }
};

UNIT_TEST_SUITE_REGISTRATION(TMisspellTest)

