#include "l2_component.h"

#include "l2_builder.h"
#include "l2_normalizer.h"
#include "l2_disk_manager.h"
#include "l2_memory_manager.h"

#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/indexer_core/index_dir.h>
#include <saas/rtyserver/indexer_core/merger_interfaces.h>
#include <saas/rtyserver/components/fullarchive/config.h>

#include <library/cpp/logger/global/global.h>

namespace NRTYServer {
    TL2ComponentBase::TL2ComponentBase(const TRTYServerConfig& config, bool isUsed)
        : IIndexComponent(isUsed)
        , Config(config)
    {
    }

    void TL2ComponentBase::CheckFullArcLayerExists() const {
        // Check that approperiate layer in full2 arch is registered
        const TRTYFullArchiveConfig* fullArcConfig = Config.ComponentsConfig.Get<TRTYFullArchiveConfig>(FULL_ARCHIVE_COMPONENT_NAME);
        AssertCorrectConfig(fullArcConfig != nullptr, "Fullarc config should be registered");

        const auto& activeLayers = fullArcConfig->GetActiveLayersFinal();
        AssertCorrectConfig(activeLayers.contains(GetName()), "Full archive should contain a layer with the same name as '%s' component", GetName().c_str());
    }

    void TL2ComponentBase::SetCore(IL2ComponentCore::TPtr core) {
        CHECK_WITH_LOG(!Core && core);

        Core = std::move(core);

        const TString componentName = GetName();
        // Replace on componentName after migration
        const auto* componentConfig = Config.ComponentsConfig.Get<TL2ComponentConfig>(componentName);
        const TString& layerName = componentConfig ? componentConfig->GetLayerName() : NFullArchive::FullLayer;
        ArcParams = MakeHolder<TL2DocStorageParams>(layerName, /*entityName=*/componentName);
        // ***

        Core->Init(Config);
        Normalizer.Reset(CreateNormalizer(Core, Config));
    }

    IL2ComponentCore::TPtr TL2ComponentBase::GetCore() const {
        CHECK_WITH_LOG(Core);
        return Core;
    }

    TString TL2ComponentBase::GetNameStatic() const {
        CHECK_WITH_LOG(Core);
        return (TString)Core->GetName();
    }

    const IIndexFilesGroup::TIndexFiles& TL2ComponentBase::GetIndexFiles() const {
        return IndexFiles;
    }

    //
    // Creator methods
    //
    THolder<IIndexComponentBuilder> TL2ComponentBase::CreateBuilder(const TBuilderConstructionContext& context) const {
        if (context.Config.GetType() == "memory" && !Core->SupportsMemorySearch())
            return nullptr;

        return DoCreateBuilder(context);
    }

    bool TL2ComponentBase::IsReadOnly() const {
        return Config.IsReadOnly;
    }

    bool TL2ComponentBase::IsIndexTypeSupported(const TManagerConstructionContext& context) const {
        switch (context.IndexType) {
            case IIndexController::MEMORY:
                return Core->SupportsMemorySearch();

            case IIndexController::FINAL:
            case IIndexController::DISK:
            case IIndexController::PREPARED:
                return true;
            default:
                FAIL_LOG("Incorrect index type");
        }
    }

    bool TL2ComponentBase::DoMergeMeta(const TMergeContext& context) const {
        const ui32 head = Core->GetNormalizerVersion();
        const TString entityName = GetName();
        for (auto&& source : context.Context.Sources) {
            const TIndexMetadataProcessor meta(source);
            const TMaybe<ui32> ver = TL2Normalizer::GetNormalizerVersion(*meta, entityName);
            if (ver && *ver != head) {
                AssertCorrectIndex(*ver == head, "Incompatible L2 data version in %s - the normalizer should have fixed it", source.c_str());
            }
        }
        CHECK_WITH_LOG(!IsReadOnly());
        for (auto&& destination : context.Context.Dests) {
            TIndexMetadataProcessor meta(destination);
            TL2Normalizer::SetNormalizerVersion(*meta, entityName, head);
        }
        return true;
    }

    bool TL2ComponentBase::DoMerge(const TMergeContext& context) const {
        CHECK_WITH_LOG(context.Context.Decoder != nullptr);
        CHECK_WITH_LOG(!IsReadOnly());

        // do nothing - FullArc merges everything for us

        // Note(yrum@): it is expected that a descendant component that builds inverted indexes or writes additional data, may want to extend this method

        return true;
    }

    bool TL2ComponentBase::DoAllRight(const TNormalizerContext& context) const {
        VERIFY_WITH_LOG(Normalizer, "incorrect operation");

        return Normalizer->AllRight(context, {});
    }

    void TL2ComponentBase::CheckAndFix(const TNormalizerContext& context) const {
        VERIFY_WITH_LOG(Normalizer, "incorrect operation");

        if (Y_UNLIKELY(IsReadOnly())) {
            AssertCorrectIndex(DoAllRight(context), "%s is corrupted and cannot be repaired in read-only mode: %s", GetName().c_str(), context.Dir.PathName().c_str());
            return;
        }

        if (!AllRight(context)) {
            Normalizer->Fix(context, {});
        }
    }

}
