#pragma once

#include "l2_config.h"
#include "l2_core.h"
#include "globals.h"

#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/indexer_core/abstract_model.h>
#include <saas/rtyserver/indexer_core/normalizer.h>

namespace NRTYServer {
    class TL2ComponentBase: public NRTYServer::IIndexComponent {
    protected:
        TIndexFiles IndexFiles; // empty
        const TRTYServerConfig& Config;
        THolder<INormalizer> Normalizer;
        THolder<TL2DocStorageParams> ArcParams;

    private:
        IL2ComponentCore::TPtr Core;

    public:
        TL2ComponentBase(const TRTYServerConfig& config, bool isUsed);

        void CheckAndFix(const TNormalizerContext& context) const override;

    public:
        bool CheckAlways() const override {
            return false;
        }

        THolder<IIndexComponentBuilder> CreateBuilder(const TBuilderConstructionContext& context) const override; // calls DoCreateBuilder
        virtual THolder<IIndexComponentBuilder> DoCreateBuilder(const TBuilderConstructionContext& context) const = 0;

        virtual INormalizer* CreateNormalizer(IL2ComponentCore::TPtr core, const TRTYServerConfig& config) const = 0;

        IL2ComponentCore::TPtr GetCore() const;

    protected:
        void CheckFullArcLayerExists() const;
        bool DoMergeMeta(const TMergeContext& context) const override;
        bool DoMerge(const TMergeContext& context) const override;
        bool DoAllRight(const TNormalizerContext& context) const override;

        bool IsReadOnly() const;
        bool IsIndexTypeSupported(const TManagerConstructionContext& context) const;

    public:
        const IIndexFilesGroup::TIndexFiles& GetIndexFiles() const override;

    protected:
        TString GetNameStatic() const;

        void SetCore(IL2ComponentCore::TPtr core);
    };

    template <typename TL2ComponentCore>
    class TL2Component: public TL2ComponentBase {
    private:
        static_assert(std::is_base_of_v<IL2ComponentCore, TL2ComponentCore>);

    public:
        explicit TL2Component(const TRTYServerConfig& config)
            : TL2ComponentBase(config, config.ComponentsSet.contains(TL2ComponentCore{}.GetName()))
        {
            SetCore(MakeIntrusive<TL2ComponentCore>());
        }

        TString GetName() const override {
            return GetNameStatic();
        }

        IParsedEntity::TPtr BuildParsedEntity(IParsedEntity::TConstructParams& params) const override {
            static_assert(std::is_base_of_v<IParsedEntity, typename TL2ComponentCore::TDocEntity>);
            return new typename TL2ComponentCore::TDocEntity(params);
        }

        IComponentParser::TPtr BuildParser() const override {
            static_assert(std::is_base_of_v<IComponentParser, typename TL2ComponentCore::TParser>);
            CheckFullArcLayerExists();
            return new typename TL2ComponentCore::TParser(GetCore());
        }

        THolder<IIndexComponentManager> CreateManager(const TManagerConstructionContext& context) const override {
            static_assert(std::is_base_of_v<IIndexComponentManager, typename TL2ComponentCore::TDiskManager>);
            static_assert(std::is_base_of_v<IIndexComponentManager, typename TL2ComponentCore::TMemoryManager>);
            if (!TL2ComponentBase::IsIndexTypeSupported(context))
                return nullptr;

            CheckFullArcLayerExists();
            if (context.IndexType == IIndexController::MEMORY) {
                return MakeHolder<typename TL2ComponentCore::TMemoryManager>(GetName(), GetCore());
            } else {
                return MakeHolder<typename TL2ComponentCore::TDiskManager>(GetName(), context.Dir.PathName(), *ArcParams, GetCore());
            }
        }

        // L2Component implementation usually do not need a custom builder, because all indexation happens in Parser
        THolder<IIndexComponentBuilder> DoCreateBuilder(const TBuilderConstructionContext& context) const override {
            static_assert(std::is_base_of_v<IIndexComponentBuilder, typename TL2ComponentCore::TBuilder>);
            THolder<IIndexComponentManager> manager;
            if (context.Config.GetType() == "memory") {
                manager.Reset(new typename TL2ComponentCore::TMemoryManager(GetName(), GetCore()));
            } else {
                manager.Reset(new typename TL2ComponentCore::TDiskManager(GetName(), context.TempDir.PathName(), *ArcParams, GetCore()));
            }
            return MakeHolder<typename TL2ComponentCore::TBuilder>(GetName(), GetName(), manager.Release(), GetCore());
        }

        INormalizer* CreateNormalizer(IL2ComponentCore::TPtr core, const TRTYServerConfig& config) const override {
            static_assert(std::is_base_of_v<INormalizer, typename TL2ComponentCore::TNormalizer>);
            return new typename TL2ComponentCore::TNormalizer(GetName(), core, config, *ArcParams);
        }
    };

}
