#include "component.h"
#include "builder.h"
#include "manager.h"
#include "parsed_entity.h"
#include "repairing_normalizer.h"
#include "sln_normalizer.h"
#include "versions_normalizer.h"

#include <saas/rtyserver/indexer_core/index_component_storage.h>
#include <saas/rtyserver/common/should_stop.h>
#include <saas/rtyserver/components/search/normalizers/invhash_normalizer.h>
#include <saas/rtyserver/components/search/normalizers/multipart_normalizer.h>
#include <saas/rtyserver/components/search/normalizers/search_archive_normalizer.h>
#include <saas/rtyserver/components/oxy/panther/normalizer.h>
#include <saas/rtyserver/components/oxy/textwad/ki_normalizer.h>
#include <saas/rtyserver/components/oxy/processors/tuples_list.h>
#include <saas/rtyserver/components/fullarchive/disk_manager.h>
#include <saas/rtyserver/components/fullarchive/component.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/common_indexers_config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/rtyserver/config/indexer_config.h>

#include <kernel/yt/logging/log.h>
#include <robot/library/oxygen/indexer/processor/pruning/interface.h>
#include <robot/library/oxygen/indexer/object_context/object_context.h>

#include <kernel/multipart_archive/multipart.h>

namespace {
    const TString OxyLockedIndexFiles[] = {
        "indexerf2", "indexfrq", "indexherf",
        "indexpanther.key", "indexpanther.inv",
        "index.docurl",
        "index.refaww", "index.refdmap", "index.reflerf", "index.refxmap",
        "indexann.key", "indexann.inv", "indexann.data", "indexann.sent",
        "indexregerf", "indexregherf",
        "indexsent", "indextitleseq", "index.urlseq",
        "indexuserdoppurldat", "indexuserdoppurlinv", "indexuserdoppurlkey",
        "indexuserurldat", "indexuserurlinv", "indexuserurlkey"
    };

    class TLogToLogBackendWrapper: public TLogBackend {
    public:
        void WriteData(const TLogRecord& rec) final {
            TLoggerOperator<TGlobalLog>::Log().Write(rec.Priority, rec.Data, rec.Len);
        }
        void ReopenLog() final {
        }
        ELogPriority FiltrationLevel() const final {
            return TLoggerOperator<TGlobalLog>::Log().FiltrationLevel();
        }

    };
}

bool TOXYIndexComponent::HasIndexFiles(const TString& path) const {
    const TRTYFullArchive* fullArc = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
    CHECK_WITH_LOG(fullArc);
    return fullArc->HasIndexFiles(path);
}

bool TOXYIndexComponent::IsFinalIndex(const TString& path) const {
    const TRTYFullArchive* fullArc = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
    CHECK_WITH_LOG(fullArc);
    return fullArc->IsFinalIndex(path);
}

bool TOXYIndexComponent::IsEmptyIndex(const TString& path) const {
    const TRTYFullArchive* fullArch = dynamic_cast<const TRTYFullArchive*>(TIndexComponentsStorage::Instance().GetComponent(FULL_ARCHIVE_COMPONENT_NAME));
    CHECK_WITH_LOG(fullArch);
    return fullArch->IsEmptyIndex(path);
}

TOXYIndexComponent::TOXYIndexComponent(const TRTYServerConfig& config)
    : TBaseIndexComponent(config, config.IndexGenerator == OXY_COMPONENT_NAME)
    , ComponentConfig(*config.ComponentsConfig.Get<TRTYOxyConfig>(OXY_COMPONENT_NAME))
    , Normalizers(config)
{
    for (auto&& file : OxyLockedIndexFiles) {
        RegisterIndexFile(TIndexFile(file, false, TIndexFile::ppLock));
    }
    RegisterIndexFile(TIndexFile("indexsln", true, TIndexFile::ppDisable));

    TuplesListsAndFilters.Reset(new TTuplesListsAndFilters(*this));
    VERIFY_WITH_LOG(!Config.GetCommonIndexers().UseSlowUpdate || TuplesListsAndFilters->GetHasFullCoverage(), "there is no full cover of tuples by fullarchive");

    const bool dbgDisableNormalizers = ComponentConfig.GetDbgOxyFlags().contains("disable_normalizers");
    if (Y_LIKELY(!dbgDisableNormalizers)) {
        if (!ComponentConfig.GetDbgOxyFlags().contains("new_indexarc")) {
            Normalizers.Register(new TSearchArchiveNormalizer(Config));
        }
        if (Config.GetSearcherConfig().ArchiveType == AT_MULTIPART) {
            Normalizers.Register(new TMultipartNormalizer(Config, "indexarc", Config.GetCommonIndexers().TextArchiveParams));
        }
        Normalizers.Register(new TOXYSlnNormalizer(Config, TuplesListsAndFilters));
        if (!ComponentConfig.GetDbgOxyFlags().contains("new_indexinvhash")) {
            Normalizers.Register(new TInvHashNormalizer(Config));
        }
        Normalizers.Register(new TPantherNormalizer(Config));
        Normalizers.Register(new TRepairingNormalizer(Config));
        Normalizers.Register(new TProcessorVersionsNormalizer(Config));
        if (!ComponentConfig.GetDbgOxyFlags().contains("newwad_external_merger")) {
            Normalizers.Register(new TOXYKeyInvNormalizer(Config));
        }
    }

    auto wrapper = MakeAtomicShared<TLogToLogBackendWrapper>();
    NOxygen::TOxygenLogger::GetInstance().SetDefault(wrapper);
    NOxygen::TOxygenLogger::GetInstance().SetLogLevel(static_cast<ELogPriority>(Config.DaemonConfig.GetLogLevel()));
}

const TRTYServerConfig& TOXYIndexComponent::GetConfig() const {
    return Config;
}

NOxygen::TTuplesUsageInfo TOXYIndexComponent::BuildTuplesInfo(bool forMerger) const {
    NOxygen::TTuplesUsageInfo result;

    for (auto&& [realmName, realmConfig] : Config.GetRealmListConfig().RealmsConfig) {
        const NOxygen::TOxygenOptions* oxygenOptions = realmConfig.GetOxygenOptions();
        if (oxygenOptions) {
            result.Merge(BuildTuplesInfo(*oxygenOptions, forMerger));
        }
    }

    return result;
}

NOxygen::TTuplesUsageInfo TOXYIndexComponent::BuildTuplesInfo(const NOxygen::TOxygenOptions& oxygenOptions, bool merger) const {
    NOxygen::TGroupAttrMap attrs;
    TProcessorsBuilderContext context(oxygenOptions);
    context.InitAttrs(attrs).SetForMerger(merger);
    CHECK_WITH_LOG(TOXYIndexBuilder::BuildProcessors(context));
    NOxygen::TProcessorPtr proc = TOXYIndexBuilder::BuildPruningProcessor(context.ProcessorsCollection, context).Release();
    if (!!proc) {
        return proc->GetRequiredTuples();
    } else {
        return context.ProcessorsCollection->GetRequiredTuples();
    }
}

THolder<NRTYServer::IIndexComponentBuilder> TOXYIndexComponent::CreateBuilder(const NRTYServer::TBuilderConstructionContext& context) const {
    if (context.Config.GetType() == "memory") {
        return nullptr;
    }

    return MakeHolder<TOXYIndexBuilder>(context.DirConfig.TempDir, GetOxygenOptions(context.RealmName), context.Config.Common.Owner, false, false, *TuplesListsAndFilters, GetName());
}

THolder<NRTYServer::IIndexComponentManager> TOXYIndexComponent::CreateManager(const NRTYServer::TManagerConstructionContext& context) const {
    CHECK_WITH_LOG(context.Config.GetOxygenOptions());

    NOxygen::TGroupAttrMap attrs;
    TProcessorsBuilderContext contextUpdaters(*context.Config.GetOxygenOptions());
    contextUpdaters.SetDir(context.Dir.PathName()).InitAttrs(attrs).SetForUpdate();

    CHECK_WITH_LOG(TOXYIndexBuilder::BuildProcessors(contextUpdaters));
    return MakeHolder<TOXYIndexManager>(context, contextUpdaters.Updaters, OXY_COMPONENT_NAME);
}

namespace {
    class IOXYArchiveIterator {
    public:
        virtual ~IOXYArchiveIterator() = default;

        virtual ui32 GetDocId() const = 0;

        virtual const TParsedDocument::TPtr GetDocument() const = 0;

        virtual bool IsValid() const = 0;

        virtual void Next() = 0;

        static THolder<IOXYArchiveIterator> Create(bool pruningOn, const TDiskFAManager* fa, const TString& layerName, ui32 clusterSize);
    };

    class TOXYPruningIterator final : public IOXYArchiveIterator {
    private:
        TDiskFAManager::TIterator::TPtr FaIterator;

    public:
        TOXYPruningIterator(const TDiskFAManager* fa, const TString& layerName)
            : FaIterator(fa->CreateIterator(layerName))
        {
        }

        ui32 GetDocId() const override {
            return FaIterator->GetDocId();
        }

        bool IsValid() const override {
            return FaIterator->IsValid();
        }

        void Next() override {
            FaIterator->Next();
        }

        const TParsedDocument::TPtr GetDocument() const override {
            return FaIterator->GetDocument();
        }
    };

    class TOXYMonotonicIterator final : public IOXYArchiveIterator {
    private:
        TDiskFAManager::TSampler::TPtr FaSampler;
        ui32 DocId;
        ui32 ClusterSize;

    public:
        TOXYMonotonicIterator(const TDiskFAManager* fa, const TString& layerName, ui32 clusterSize)
            : FaSampler(fa->CreateSampler(layerName))
            , DocId(0)
            , ClusterSize(clusterSize)
        {
            Init();
        }

        ui32 GetDocId() const override {
            Y_ASSERT(DocId == FaSampler->GetDocId());
            return FaSampler->GetDocId();
        }

        bool IsValid() const override {
            return DocId < ClusterSize;
        }

        void Next() override {
            while (++DocId < ClusterSize) {
                if (Y_LIKELY(FaSampler->Seek(DocId))) {
                    return;
                }
            }
        }

        const TParsedDocument::TPtr GetDocument() const override {
            return FaSampler->GetDocument();
        }
    private:
        void Init() {
            FaSampler->Seek(0);
            if (!FaSampler->IsValid()) {
                Next();
            }
        }
    };

    THolder<IOXYArchiveIterator> IOXYArchiveIterator::Create(bool pruningOn, const TDiskFAManager* fa, const TString& layerName, ui32 clusterSize) {
        if (pruningOn) {
            return MakeHolder<TOXYPruningIterator>(fa, layerName);
        } else {
            return MakeHolder<TOXYMonotonicIterator>(fa, layerName, clusterSize);
        }
    }
}

bool TOXYIndexComponent::DoMerge(const NRTYServer::TMergeContext& context) const {
    TVector<TAutoPtr<TOXYIndexBuilder>> dsts;

    TVector<TVector<ui32>> dstRemap(context.Context.Dests.size());
    TVector<ui32> dstRemapCounter(context.Context.Dests.size(), 0);

    NOxygen::TGroupAttrMap attrs;

    const bool pruningOn = Config.Pruning && Config.Pruning->PruningOn();

    const NOxygen::TOxygenOptions& oxygenOptions = GetOxygenOptions(context.RealmName);
    TProcessorsBuilderContext procMergers(oxygenOptions);
    procMergers.InitAttrs(attrs).SetForMerger().SetTuples(TuplesListsAndFilters.Get());
    CHECK_WITH_LOG(TOXYIndexBuilder::BuildProcessors(procMergers));

    const bool rebuildFromArchive = TuplesListsAndFilters->GetRequiredTuples(true).GetSize() || pruningOn;

    for (ui32 dst = 0; dst < context.Context.Dests.size(); ++dst) {
        dstRemap[dst].resize(context.Context.Decoder->GetNewDocsCount(dst), Max<ui32>());
        if (context.Task)
            context.Task->AddProgressInfo("oxy merge builder starting for " + context.Context.Dests[dst] + "...");

        dsts.push_back(new TOXYIndexBuilder(context.Context.Dests[dst], oxygenOptions, Config, /*pruneOnly=*/false, true, *TuplesListsAndFilters, GetName()));
        if (!dsts.back()->Start()) {
            ERROR_LOG << "Cannot start Oxygen builder for " << context.Context.Dests[dst] << Endl;
            return false;
        }
        if (context.Task)
            context.Task->AddProgressInfo("oxy merge builder starting for " + context.Context.Dests[dst] + "...OK");
    }

    for (ui32 src = 0; src < context.Context.Sources.size() && !ShouldStop(context.RigidStopSignal); ++src) {
        TString srcDir = context.Context.Sources[src];
        if (context.Task)
            context.Task->AddProgressInfo("Processing " + srcDir + "...(" + ToString(src) + "/" + ToString(context.Context.Sources.size()) + "/" + ToString(context.Context.Decoder->GetSizeOfCluster(src)) + ")");
        const TDiskFAManager* srcFullArc = context.Merger->GetIndexStorage().GetIndexController(srcDir)->GetManagers().GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME);
        CHECK_WITH_LOG(srcFullArc);
        ui32 counter = 0;
        ui32 percentReport = 0;

        const ui32 srcSize = context.Context.Decoder->GetSizeOfCluster(src);

        if (Y_LIKELY(rebuildFromArchive)) {
            const TString& layerForMerge = TuplesListsAndFilters->GetLayerForMerge();
            for (auto i = IOXYArchiveIterator::Create(pruningOn, srcFullArc, layerForMerge, srcSize); i->IsValid() && !ShouldStop(context.RigidStopSignal); i->Next()) {
                TRTYMerger::TAddress addr = context.Context.Decoder->Decode(src, i->GetDocId());
                // DEBUG_LOG << "OB " << src << "-" << i->GetDocId() << " to " << addr.ClusterId << "-" << addr.DocId << Endl;
                if (addr.DocId == REMAP_NOWHERE)
                    continue;

                auto doc = i->GetDocument();
                AssertCorrectIndex(!!doc, "Incorrect FullArchive doc: %s docid %d", context.Context.Sources[addr.ClusterId].data(), addr.DocId);
                dsts[addr.ClusterId]->Index(0, *doc, dstRemapCounter[addr.ClusterId]++);
                VERIFY_WITH_LOG(!doc->IsFailed(), "cannot index Oxygen document on merge: %s", doc->GetErrorsCollector().GetStringReport().data());
                dstRemap[addr.ClusterId][dstRemapCounter[addr.ClusterId] - 1] = addr.DocId;
                ui32 currentPercent = (counter * 100) / context.Context.Decoder->GetSizeOfCluster(src);
                if (context.Task && percentReport != currentPercent) {
                    context.Task->SetProgressInfo(srcDir + ":" + ToString(counter) + "/" + ToString(context.Context.Decoder->GetSizeOfCluster(src)) + ": " + ToString(currentPercent) + "%");
                    percentReport = currentPercent;
                }
                ++counter;
            }
        } else {
            // Do nothing but set remappings from Decoder, adding the deletions from FullArc BaseLayer
            auto archiveDocIds = srcFullArc->GetDocIds();
            for (size_t i = 0; i < archiveDocIds.size() && !ShouldStop(context.RigidStopSignal); ++i) {
                const auto docId = archiveDocIds[i];
                TRTYMerger::TAddress addr = context.Context.Decoder->Decode(src, docId);
                if (addr.DocId == REMAP_NOWHERE)
                    continue;
                dsts[addr.ClusterId]->IndexEmpty(0, dstRemapCounter[addr.ClusterId]++); // IndexEmpty still stores some stats for GetResortMapForMerge call below
                dstRemap[addr.ClusterId][dstRemapCounter[addr.ClusterId] - 1] = addr.DocId;
                ++counter;
            }
        }

        if (context.Task)
            context.Task->AddProgressInfo("Processing " + srcDir + "...(" + ToString(src) + "/" + ToString(context.Context.Sources.size()) + ") OK");
    }
    if (context.Task)
        context.Task->SetProgressInfo("");

    for (ui32 dst = 0; dst < context.Context.Dests.size() && !ShouldStop(context.RigidStopSignal); ++dst) {
        TVector<ui32> docsMap;
        TVector<ui32> docsMapResult;
        TDirConf dirConf;
        dirConf.TempDir = context.Context.Dests[dst];
        if (context.Task) {
            context.Task->SetProgressInfo("oxy merge builder decode patch using for " + dirConf.TempDir + "...");
        }
        dsts[dst]->GetResortMapForMerge(nullptr, docsMap);
        CHECK_WITH_LOG(context.Context.Decoder->GetNewDocsCount(dst) >= docsMap.size());
        docsMapResult.resize(context.Context.Decoder->GetNewDocsCount(dst), -1);
        for (ui32 i = 0; i < docsMap.size(); ++i) {
            if (dstRemap[dst][i] != Max<ui32>()) {
                CHECK_WITH_LOG(dstRemap[dst][i] < docsMapResult.size());
                docsMapResult[dstRemap[dst][i]] = docsMap[i];
            }
        }
        context.Context.Decoder->PatchDestMap(dst, docsMapResult);
        if (context.Task)
            context.Task->AddProgressInfo("oxy merge builder decode patch using for " + dirConf.TempDir + "...OK");

        if (context.Task)
            context.Task->AddProgressInfo("oxy merge builder stopping and close for " + dirConf.TempDir + "...");
        if (!dsts[dst]->Stop()) {
            ERROR_LOG << "Cannot stop Oxygen builder for " << context.Context.Dests[dst] << Endl;
            return false;
        }
        const TPathName dirName(dirConf.TempDir);
        NRTYServer::TBuilderCloseContext fakeContext(dirName, dirName, nullptr, context.RigidStopSignal);
        if (!dsts[dst]->Close(fakeContext)) {
            ERROR_LOG << "Cannot close Oxygen builder for " << context.Context.Dests[dst] << Endl;
            return false;
        }
        if (context.Task)
            context.Task->AddProgressInfo("oxy merge builder stopping and close for " + dirConf.TempDir + "...OK");
    }

    TStringBuf realmStr(context.Realm == NRTYServer::ERealm::Realtime ? " (RT)" : "");

    for (auto&& i : procMergers.MergersWithoutUpdaters) {
        bool ok = i->Merge(context, Config);
        if (ShouldStop(context.RigidStopSignal))
            break;
        AssertCorrectIndex(ok, "Can't merge the indexes without updaters%s", realmStr.data());
    }

    if (!ShouldStop(context.RigidStopSignal)) {
        context.Context.Callback->OnBeforeUpdatableMerging();
        for (auto&& i : procMergers.MergersWithUpdaters) {
            bool ok = i->Merge(context, Config);
            if (ShouldStop(context.RigidStopSignal))
                break;
            AssertCorrectIndex(ok, "Can't merge the indexes with updaters%s", realmStr.data());
        }
    }

    if (ShouldStop(context.RigidStopSignal)) {
        WARNING_LOG << "merger interrupted" << Endl;
        if (context.Task)
            context.Task->AddProgressInfo("merger interrupted");
    }

    return !ShouldStop(context.RigidStopSignal);
}

bool TOXYIndexComponent::DoMergeMeta(const NRTYServer::TMergeContext& context) const {
    NOxygen::TTuplesListProcessor::TTupleNameSetByLayers resultTuples;
    bool initialized = false;
    for (ui32 src = 0; src < context.Context.Sources.size(); ++src) {
        NOxygen::TTuplesListProcessor::TTupleNameSetByLayers srcTuples;
        if (!NOxygen::TTuplesListProcessor::LoadTuplesList(context.Context.Sources[src], srcTuples))
            continue;
        if (initialized) {
            resultTuples.Intersect(srcTuples);
        } else {
            initialized = true;
            resultTuples = srcTuples;
        }
    }

    for (ui32 dst = 0; dst < context.Context.Dests.size(); ++dst)
        NOxygen::TTuplesListProcessor::SaveTuplesList(context.Context.Dests[dst], resultTuples);
    return TBaseIndexComponent::DoMergeMeta(context);
}

NRTYServer::IComponentParser::TPtr TOXYIndexComponent::BuildParser() const {
    TVector<IOxyUpdater::TPtr> updaters;
    auto fillUpdaters = [&](const NOxygen::TOxygenOptions* oxygenOptions) {
        NOxygen::TGroupAttrMap attrs;
        TProcessorsBuilderContext context(*oxygenOptions);
        context.InitAttrs(attrs).SetForUpdate();
        CHECK_WITH_LOG(TOXYIndexBuilder::BuildProcessors(context));
        updaters.insert(updaters.end(), context.Updaters.begin(), context.Updaters.end());
    };

    bool hasOxygenOptions = false;
    for (auto&& [realmName, realmConfig] : Config.GetRealmListConfig().RealmsConfig) {
        bool unique = true;
        for (auto&& [realmN, realmC] : Config.GetRealmListConfig().RealmsConfig) {
            if (realmN == realmName) {
                break;
            }
            if (realmC.GetOxygenOptions() == realmConfig.GetOxygenOptions()) {
                unique = false;
                break;
            }
        }
        const NOxygen::TOxygenOptions* oxygenOptions = realmConfig.GetOxygenOptions();
        if (oxygenOptions && unique) {
            hasOxygenOptions = true;
            fillUpdaters(oxygenOptions);
        } else if (!hasOxygenOptions) {
            return nullptr;
        }
    }

    return new TOXYComponentParser(*this, updaters);
}

NRTYServer::IParsedEntity::TPtr TOXYIndexComponent::BuildParsedEntity(NRTYServer::IParsedEntity::TConstructParams& params) const {
    return new TOXYParsedEntity(params);
}

void TOXYIndexComponent::CheckTuples(const TString& tuples) const {
    NOxygen::TObjectContext oc(tuples);
    auto result = TuplesListsAndFilters->GetRequiredTuples(false).VerifyObject(oc);
    if (!result.Correct) {
        throw yexception() << "KiwiObject does not have required tuple groups: " << JoinSeq(" ", result.MissingTupleGroups);
    }
}

void TOXYIndexComponent::CheckAndFix(const NRTYServer::TNormalizerContext& context) const {
    const THolder<TFileMap>& fileIndexFrq = context.Managers.GetManager<TOXYIndexManager>(OXY_COMPONENT_NAME)->GetFileIndexFrq();
    Normalizers.CheckAndFix(context, fileIndexFrq);
}

bool TOXYIndexComponent::DoAllRight(const NRTYServer::TNormalizerContext& context) const {
    const THolder<TFileMap>& fileIndexFrq = context.Managers.GetManager<TOXYIndexManager>(OXY_COMPONENT_NAME)->GetFileIndexFrq();
    return Normalizers.AllRight(context, fileIndexFrq);
}

bool TOXYIndexComponent::CheckConfig() const {
    bool result = TBaseIndexComponent::CheckConfig();

    if (!Config.GetCommonIndexers().OxygenOptions) {
        ERROR_LOG << "Oxy component without OxygenConfig" << Endl;
        result = false;
    }

    const bool dbgSkipOxyOptionsCheck = ComponentConfig.GetDbgOxyFlags().contains("skip_oxygen_options_check");
    if (!Config.GetCommonIndexers().OxygenOptions->HasKeyInvOptions() && !Config.GetCommonIndexers().OxygenOptions->HasTextWadOptions() ) {
        ERROR_LOG << "Oxy component without KeyInvOptions or TextWadOptions in OxygenConfig" << Endl;
        result = false || dbgSkipOxyOptionsCheck;
    }
    if (!ComponentConfig.GetDbgOxyFlags().contains("new_indexarc") && !Config.GetCommonIndexers().OxygenOptions->HasArcOptions()) {
        ERROR_LOG << "Oxy component without ArcOptions in OxygenConfig" << Endl;
        result = false || dbgSkipOxyOptionsCheck;
    }

    return result;
}

class TOXYPruningCalcer: public TPruningConfig::ICalcer {
public:
    TOXYPruningCalcer(const TOXYIndexManager* manager)
        : Manager(manager)
    {}

    double PruningRank(ui32 docid) const override {
        VERIFY_WITH_LOG(Manager, "Invalid usage");
        return static_cast<double>(Manager->PrnValue(docid, 0));
    }

private:
    const TOXYIndexManager* Manager;
};

THolder<TPruningConfig::ICalcer> TOXYIndexComponent::CreatePruningCalcer(const NRTYServer::IIndexManagersStorage* managers) const {
    if (Config.Pruning->GetType() == TPruningConfig::OXY)
        return MakeHolder<TOXYPruningCalcer>(managers ? managers->GetManager<TOXYIndexManager>(OXY_COMPONENT_NAME) : nullptr);
    return TBaseIndexComponent::CreatePruningCalcer(managers);
}

const TTuplesListsAndFilters::TTupleNameSet& TOXYIndexComponent::GetFilterTuples() const {
    return TuplesListsAndFilters->GetFilterTuples();
}

const TTuplesListsAndFilters::TTupleNameSet* TOXYIndexComponent::GetFilterTuples(const TString& layer) const {
    if (!layer)
        return nullptr;
    return TuplesListsAndFilters->GetFilterTuples(layer);
}

bool TOXYIndexComponent::CheckAlways() const {
    return true;
}

TTuplesListsAndFilters::TTupleNameSet TOXYIndexComponent::GetAdditionalRequiredTuples() const {
    return ComponentConfig.GetAdditionalRequiredTuples();
}

TTuplesListsAndFilters::TTupleNameSet TOXYIndexComponent::GetAdditionalRequiredMergeTuples() const {
    return ComponentConfig.GetAdditionalRequiredMergeTuples();
}

const NOxygen::TOxygenOptions& TOXYIndexComponent::GetOxygenOptions(const TString& realmName) const {
    const NOxygen::TOxygenOptions* oxyOptions = Config.GetRealmListConfig().GetRealmConfigByConfigName(realmName).GetOxygenOptions();
    if (!oxyOptions) {
        ythrow yexception() << "No OxygenOptions for realm: " << realmName;
    }
    return *oxyOptions;
}
