#include <balancer/kernel/memory/split.h>
#include <library/cpp/testing/unittest/registar.h>
#include <util/string/join.h>

Y_UNIT_TEST_SUITE(TChunkListSplitTest) {
    static auto GenChunkList(TVector<TString> chunks) {
        NSrvKernel::TChunkList res;
        for (auto&& ch : chunks) {
            res.Push(ch);
        }
        return res;
    }

    static bool HasEmptyChunks(const NSrvKernel::TChunkList& lst) {
        return std::any_of(lst.ChunksBegin(), lst.ChunksEnd(), [](const auto& chunk) {
            return chunk.Length() == 0;
        });
    }

    TString FormatTest(ui32 off, TVector<TString> chList) {
        return TStringBuilder() << "off=" << off << ", exp=[" << JoinSeq(",", chList) << "]";
    }

    void DoTestCutPrefix(ui32 off, TVector<TString> chList, ui32 preSize, TStringBuf preVal, ui32 lstSize, TStringBuf lstVal) {
        auto lst = GenChunkList(chList);
        auto pre = CutPrefix(off, lst);
        UNIT_ASSERT(!HasEmptyChunks(pre));
        UNIT_ASSERT_VALUES_EQUAL_C(pre.ChunksCount(), preSize, FormatTest(off, chList));
        UNIT_ASSERT_VALUES_EQUAL_C(lst.ChunksCount(), lstSize, FormatTest(off, chList));
        UNIT_ASSERT_VALUES_EQUAL_C(pre, preVal, FormatTest(off, chList));
        UNIT_ASSERT_VALUES_EQUAL_C(lst, lstVal, FormatTest(off, chList));
    }

    Y_UNIT_TEST(TestCutPrefix1) {
        using namespace NSrvKernel;

        DoTestCutPrefix(0, {}, 0, "", 0, "");
        DoTestCutPrefix(0, {"a"}, 0, "", 1, "a");
        DoTestCutPrefix(1, {"a"}, 1, "a", 0, "");
        DoTestCutPrefix(0, {"", "a"}, 0, "", 1, "a");
        DoTestCutPrefix(1, {"", "a"}, 1, "a", 0, "");

        DoTestCutPrefix(0, {"a", ""}, 0, "", 1, "a");
        DoTestCutPrefix(1, {"a", ""}, 1, "a", 0, "");

        DoTestCutPrefix(1, {"ab"}, 1, "a", 1, "b");
        DoTestCutPrefix(1, {"a", "", "b"}, 1, "a", 1, "b");
        DoTestCutPrefix(2, {"a", "", "b"}, 2, "ab", 0, "");
    }

    void DoTestPortion(NSrvKernel::TChunkList lst, ui32 portionSize, TString total) {
        auto fullLength = lst.size();
        auto portion = NSrvKernel::CutPrefix(portionSize, lst);
        UNIT_ASSERT_VALUES_EQUAL(portion.size(), std::min<ui32>(portionSize, fullLength));
        UNIT_ASSERT_VALUES_EQUAL(portion.size() + lst.size(), fullLength);
        UNIT_ASSERT(!HasEmptyChunks(portion));
        portion.Append(std::move(lst));
        UNIT_ASSERT_VALUES_EQUAL((TStringBuilder() << portion), total);
    }

    void DoTestChunkList(std::initializer_list<TString> lst) {
        auto total = JoinSeq("", lst);
        for (size_t portionSize = 0; portionSize < total.size() + 2; ++portionSize) {
            DoTestPortion(GenChunkList(lst), portionSize, total);
        }
    }

    Y_UNIT_TEST(TestCutPrefix2) {
        DoTestChunkList({});
        DoTestChunkList({""});
        DoTestChunkList({"a"});
        DoTestChunkList({"ab"});
        DoTestChunkList({"abc"});
        DoTestChunkList({"", ""});
        DoTestChunkList({"a", ""});
        DoTestChunkList({"ab", ""});
        DoTestChunkList({"abc", ""});
        DoTestChunkList({"", "1"});
        DoTestChunkList({"a", "1"});
        DoTestChunkList({"ab", "1"});
        DoTestChunkList({"abc", "1"});
        DoTestChunkList({"", "12"});
        DoTestChunkList({"a", "12"});
        DoTestChunkList({"ab", "12"});
        DoTestChunkList({"abc", "12"});
        DoTestChunkList({"", "123"});
        DoTestChunkList({"a", "123"});
        DoTestChunkList({"ab", "123"});
        DoTestChunkList({"abc", "123"});
        DoTestChunkList({"", "", ""});
        DoTestChunkList({"a", "", ""});
        DoTestChunkList({"ab", "", ""});
        DoTestChunkList({"abc", "", ""});
        DoTestChunkList({"", "1", ""});
        DoTestChunkList({"a", "1", ""});
        DoTestChunkList({"ab", "1", ""});
        DoTestChunkList({"abc", "1", ""});
        DoTestChunkList({"", "12", ""});
        DoTestChunkList({"a", "12", ""});
        DoTestChunkList({"ab", "12", ""});
        DoTestChunkList({"abc", "12", ""});
        DoTestChunkList({"", "123", ""});
        DoTestChunkList({"a", "123", ""});
        DoTestChunkList({"ab", "123", ""});
        DoTestChunkList({"abc", "123", ""});
        DoTestChunkList({"", "", "x"});
        DoTestChunkList({"a", "", "x"});
        DoTestChunkList({"ab", "", "x"});
        DoTestChunkList({"abc", "", "x"});
        DoTestChunkList({"", "1", "x"});
        DoTestChunkList({"a", "1", "x"});
        DoTestChunkList({"ab", "1", "x"});
        DoTestChunkList({"abc", "1", "x"});
        DoTestChunkList({"", "12", "x"});
        DoTestChunkList({"a", "12", "x"});
        DoTestChunkList({"ab", "12", "x"});
        DoTestChunkList({"abc", "12", "x"});
        DoTestChunkList({"", "123", "x"});
        DoTestChunkList({"a", "123", "x"});
        DoTestChunkList({"ab", "123", "x"});
        DoTestChunkList({"abc", "123", "x"});
        DoTestChunkList({"", "", "xy"});
        DoTestChunkList({"a", "", "xy"});
        DoTestChunkList({"ab", "", "xy"});
        DoTestChunkList({"abc", "", "xy"});
        DoTestChunkList({"", "1", "xy"});
        DoTestChunkList({"a", "1", "xy"});
        DoTestChunkList({"ab", "1", "xy"});
        DoTestChunkList({"abc", "1", "xy"});
        DoTestChunkList({"", "12", "xy"});
        DoTestChunkList({"a", "12", "xy"});
        DoTestChunkList({"ab", "12", "xy"});
        DoTestChunkList({"abc", "12", "xy"});
        DoTestChunkList({"", "123", "xy"});
        DoTestChunkList({"a", "123", "xy"});
        DoTestChunkList({"ab", "123", "xy"});
        DoTestChunkList({"abc", "123", "xy"});
        DoTestChunkList({"", "", "xyz"});
        DoTestChunkList({"a", "", "xyz"});
        DoTestChunkList({"ab", "", "xyz"});
        DoTestChunkList({"abc", "", "xyz"});
        DoTestChunkList({"", "1", "xyz"});
        DoTestChunkList({"a", "1", "xyz"});
        DoTestChunkList({"ab", "1", "xyz"});
        DoTestChunkList({"abc", "1", "xyz"});
        DoTestChunkList({"", "12", "xyz"});
        DoTestChunkList({"a", "12", "xyz"});
        DoTestChunkList({"ab", "12", "xyz"});
        DoTestChunkList({"abc", "12", "xyz"});
        DoTestChunkList({"", "123", "xyz"});
        DoTestChunkList({"a", "123", "xyz"});
        DoTestChunkList({"ab", "123", "xyz"});
        DoTestChunkList({"abc", "123", "xyz"});
    }

};
