#include <balancer/modules/cgi_hasher/calc.h>

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

Y_UNIT_TEST_SUITE(TCgiHasherTest) {

    auto GenChunkList(TString res) {
        return NSrvKernel::TChunkList(res);
    }

    auto GenHash(TString s) {
        TMurmurHash2A<ui64> hash;
        hash.Update(s.data(), s.size());
        return hash.Value();
    }

    void DoTestNoCgi(NModCgiHasher::ICgiHasher::EMode mode) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo"},
            {mode, false}
        );

        UNIT_ASSERT(!hasher->Calc(TStringBuf()));
        UNIT_ASSERT(!hasher->Calc(TStringBuf("&bar=baz")));
        UNIT_ASSERT(!hasher->Calc(TStringBuf("?bar=baz")));
    }

    Y_UNIT_TEST(TestNoCgiConcatenated) {
        DoTestNoCgi(NModCgiHasher::ICgiHasher::EMode::Concatenated);
    }

    Y_UNIT_TEST(TestNoCgiPriority) {
        DoTestNoCgi(NModCgiHasher::ICgiHasher::EMode::Priority);
    }

    void DoTestSingleCgi(NModCgiHasher::ICgiHasher::EMode mode) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo"},
            {mode, false}
        );

        UNIT_ASSERT(!hasher->Calc(TStringBuf("foo=123")));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("&foo=123")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123&bar=baz")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?bar=baz&foo=123")), GenHash("123"));
    }

    Y_UNIT_TEST(TestSingleCgiConcatenated) {
        DoTestSingleCgi(NModCgiHasher::ICgiHasher::EMode::Concatenated);
    }

    Y_UNIT_TEST(TestSingleCgiPriority) {
        DoTestSingleCgi(NModCgiHasher::ICgiHasher::EMode::Priority);
    }

    void DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode mode) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo"},
            {mode, true}
        );

        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?Foo=123")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("&Foo=123")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?Foo=123&bar=baz")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?bar=baz&Foo=123")), GenHash("123"));
    }

    Y_UNIT_TEST(TestSingleCgiNoCaseConcatenated) {
        DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode::Concatenated);
    }

    Y_UNIT_TEST(TestSingleCgiNoCaseFirstFound) {
        DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode::Concatenated);
        DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode::Priority);
    }

    Y_UNIT_TEST(TestSingleCgiNoCasePriority) {
        DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode::Concatenated);
        DoTestSingleCgiNoCase(NModCgiHasher::ICgiHasher::EMode::Priority);
    }

    Y_UNIT_TEST(TestDuplicateCgiConcatenated) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo"},
            {NModCgiHasher::ICgiHasher::EMode::Concatenated, false}
        );

        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123&foo=567")), GenHash("123567"));
    }

    Y_UNIT_TEST(TestDuplicateCgiPriority) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo"},
            {NModCgiHasher::ICgiHasher::EMode::Priority, false}
        );

        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123&foo=567")), GenHash("123"));
    }

    Y_UNIT_TEST(TestMultipleCgiConcatenated) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo", "bar"},
            {NModCgiHasher::ICgiHasher::EMode::Concatenated, false}
        );

        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123&bar=567")), GenHash("123567"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?bar=123&foo=567")), GenHash("123567"));
    }

    Y_UNIT_TEST(TestMultipleCgiPriority) {
        auto hasher = NModCgiHasher::MakeHasher(
            {"foo", "bar"},
            {NModCgiHasher::ICgiHasher::EMode::Priority, false}
        );

        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?foo=123&bar=567")), GenHash("123"));
        UNIT_ASSERT_VALUES_EQUAL(*hasher->Calc(TStringBuf("?bar=123&foo=567")), GenHash("567"));
    }

    Y_UNIT_TEST(TestInvalid) {
        UNIT_ASSERT_EXCEPTION(
            NModCgiHasher::MakeHasher({"("}, {}),
            NSrvKernel::TRE2Error
        );
    }
}
