#include <saas/rtyserver_jupi/library/textwad/ut/textwad_large_ut.h>
#include <saas/rtyserver_jupi/library/textwad/wad_keyinv_merge_impl.h>
#include <kernel/doom/key/key_decoder.h>
#include <kernel/doom/offroad_wad/offroad_ann_wad_io.h>
#include <kernel/doom/offroad_doc_wad/offroad_factor_ann_doc_wad_io.h>
#include <kernel/doom/offroad_key_wad/offroad_key_wad_io.h>
#include <kernel/doom/adaptors/multi_key_index_writer.h>
#include <kernel/doom/adaptors/encoding_index_writer.h>
#include <kernel/doom/standard_models_storage/standard_models_storage.h>
#include <library/cpp/testing/unittest/registar.h>
#include <util/generic/vector.h>
#include <util/generic/array_ref.h>
#include <util/stream/str.h>


class TFusionTextWadTestBase {
public:
    const TStringBuf DisplaySep = "; ";

    TString DumpKeys(IWad* wad) {
        using TKeyIo = TOffroadFactorAnnKeyWadIo;
        TString result;
        TKeyIo::TReader rd(wad);
        TStringOutput ss(result);

        TStringBuf key;
        ui32 termId;
        while (rd.ReadKey(&key, &termId)) {
            TDecodedKey decodedKey;
            TKeyDecoder().Decode(key, &decodedKey);
            ss << decodedKey << "=" << termId << DisplaySep;
        }
        ss.Finish();
        return result;
    }

    TString DumpDocHits(IWad* wad) {
        using TDocIo = TOffroadFactorAnnDocWadIo;
        TString result;
        TDocIo::TReader rd(wad);

        TStringOutput ss(result);
        ui32 docId;
        while (rd.ReadDoc(&docId)) {
            TReqBundleHit hit;
            ss << docId << ":";
            while (rd.ReadHit(&hit)) {
                ss << hit;
            }
            ss << DisplaySep;
        }
        ss.Finish();
        return result;
    }

    template<typename TWriterIo>
    void WriteGlobalKeyHit(const TVector<TGlobalItem>& testData, const TString& fileName) {
        using TWriter = typename TWriterIo::TWriter;
        typename TWriter::TKeyModel keyModel = TStandardIoModelsStorage::Model<typename TWriter::TKeyModel>(TWriterIo::DefaultKeyModel);
        typename TWriter::THitModel hitModel = TStandardIoModelsStorage::Model<typename TWriter::THitModel>(TWriterIo::DefaultHitModel);
        THolder<TWriter> wr = MakeHolder<TWriter>(hitModel, keyModel, fileName);
        for (const auto& i : testData) {
            for (auto&& h : i.Hits)
                wr->WriteHit(h);
            TString encodedKey;
            Y_VERIFY(TKeyEncoder().Encode(i.Key, &encodedKey));
            wr->WriteKey(encodedKey);
        }
        wr->Finish();
    }
};

static_assert(std::is_same<TOffroadAnnWadIoSortedMultiKeys::TWriter::THit, TReqBundleHit>::value);
static_assert(std::is_same<TOffroadFactorAnnDocWadIo::TWriter::THit, TReqBundleHit>::value);

class TFusionTextWadMergeTest: public NUnitTest::TTestBase, public TFusionTextWadTestBase {
    UNIT_TEST_SUITE(TFusionTextWadMergeTest)
        UNIT_TEST(TestIoFormats);
    UNIT_TEST_SUITE_END();

    void TestIoFormats() {
        //
        // Checks that hi-level and low-level IO matches,
        // and that the semanics of Hit (TermId in DocId field, etc)
        // has not changed
        //
        using TStdWriterIo = NDoom::TOffroadAnnWadIoSortedMultiKeys;
        using TItem = TGlobalItem;

        {
            // This block confirms that we can't use NDoom::TOffroadAnnWadIoSortedMultiKeys for merging
            // when we have 2 forms, we fail

            auto factorAnnKey = TItem("Aaa").AddForm("Aaa").AddForm("Aaac"); // Ooops, bad form "Aaa"
            factorAnnKey.SetHits( { TReqBundleHit(0, 0, 0, 0, 0, 0), TReqBundleHit(0, 0, 0, 0, 1, 0)} );

            WriteGlobalKeyHit<TStdWriterIo>( { factorAnnKey } , "fail.wad" ); // writer silently skips the key of that kind
            THolder<IWad> wad1 = IWad::Open("fail.wad", false);
            TString actualKeys = DumpKeys(wad1.Get());
            Cerr << actualKeys << Endl;
            UNIT_ASSERT(actualKeys.empty()); //if it were !empty(), we could have avoided all that coding
        }

        // there should be 0 or 1 forms
        TVector<TItem> testData = {
            TItem("Aaa").AddForm("Aaab").SetHits( { TReqBundleHit(1, 10, 10, 0, 0, 0) }),
            TItem("Aaa").SetHits( { TReqBundleHit(0, 11, 11, 0, 0, 0), TReqBundleHit(1, 12, 12, 0, 0, 0) }),
            TItem("bbb").SetHits( { TReqBundleHit(1, 10, 10, 0, 0, 0), TReqBundleHit(2, 20, 10, 0, 0, 0) })
        };

        WriteGlobalKeyHit<TStdWriterIo>(testData, "std.wad");

        THolder<IWad> wad = IWad::Open("std.wad", false);

        TString actualKeys = DumpKeys(wad.Get());

        UNIT_ASSERT_EQUAL("Aaa (Aaa, Aaab)=0; bbb=1; ", actualKeys);

        TString actualHits = DumpDocHits(wad.Get());

        UNIT_ASSERT_EQUAL("0:[0.0.11.11.0.0]; 1:[0.0.10.10.0.1][0.0.12.12.0.0][1.0.10.10.0.0]; 2:[1.0.20.10.0.0]; ", actualHits); //hit#2 has formid, hit #5 has termid
    }
};


UNIT_TEST_SUITE_REGISTRATION(TFusionTextWadMergeTest);
