#pragma once

#include "builder.h"
#include "index.h"
#include "manager.h"
#include <saas/rtyserver/model/realm/realm.h>

#include <saas/rtyserver/config/fwd.h>
#include <saas/rtyserver/config/const.h>
#include <saas/rtyserver/config/layers.h>
#include <saas/rtyserver/config/searcher_config.h>

#include <saas/rtyserver/indexer_core/merger_interfaces.h>
#include <saas/rtyserver/search/source/search_source.h>
#include <saas/rtyserver/indexer_core/index_metadata_processor.h>
#include <saas/rtyserver/common/structured_info.h>

#include <saas/protos/rtyserver.pb.h>

#include <util/generic/ptr.h>

class TRTYIndexData;
struct TBaseConfig;
struct TDirConf;

namespace NRTYServer {
    class IIndexComponent;
    class TParsedDoc;

    class TComponentIsUnusedException : public yexception {
    };

    struct TDocSerializeContext {
        inline explicit TDocSerializeContext(const TString& layer = NRTYServer::NFullArchive::FullLayer)
            : Layer(layer)
        {}

        inline const TString& GetLayer() const {
            return Layer;
        }

    private:
        TString Layer;
    };

    using TDocParseContext = TDocSerializeContext;

    class IParsedEntity: public TNonCopyable {
    public:
        typedef TAtomicSharedPtr<IParsedEntity> TPtr;
        struct TConstructParams {
            inline TConstructParams(const IIndexComponent& component, TParsedDocument& owner)
                : Owner(owner)
                , Component(component)
            {}
            TParsedDocument& Owner;
            const IIndexComponent& Component;
        };
    public:
        virtual ~IParsedEntity() {}
        virtual bool GetGroupAttrValue(const TString& name, i64& result) const = 0;
        virtual bool FillFactorStorage(TFactorView& factorStorage) const = 0;
        virtual void MergeToProto(NRTYServer::TParsedDoc& pd, const TDocSerializeContext& context) const = 0;
        virtual bool FillFromProto(const NRTYServer::TParsedDoc& pd, const NRTYServer::TDocParseContext& context) = 0;
        virtual void SetDocSearchInfo(const TDocSearchInfo& docInfo) = 0;
        virtual void SetDocId(ui32 docid) = 0;
        virtual void ApplyPatch(const TParsedDocument& doc) = 0;
        virtual ui32 GetHash() const;
    };

    using TShard = size_t;
}

namespace NRTYServer {
    struct TBuilderConstructionContext {
        const NRTYServer::TIndexerConfig& Config;
        const TDirConf& DirConfig;
        const TBaseConfig& BaseConfig;
        const TPathName TempDir;
        const ERealm Realm;
        const TString RealmName;

        TBuilderConstructionContext(const NRTYServer::TIndexerConfig& config, const TDirConf& dirConfig, const TBaseConfig& baseConfig, const TPathName& tempDir, ERealm realm, const TString& realmName)
            : Config(config)
            , DirConfig(dirConfig)
            , BaseConfig(baseConfig)
            , TempDir(tempDir)
            , Realm(realm)
            , RealmName(realmName)
        {}
    };

    struct TManagerConstructionContext {
        const NRTYServer::TIndexerConfig& Config;
        TPathName Dir;
        IIndexOwner& Index;
        IIndexController::EIndexType IndexType;
        bool UseGlobalFileMapping;
        bool IsReadOnly;
        EExecutionContext ExecContext;

        TManagerConstructionContext(const NRTYServer::TIndexerConfig& config,
            const TPathName& dir, IIndexOwner& index,
            IIndexController::EIndexType indexType, bool useGlobalFileMapping, bool isReadOnly, EExecutionContext execCtx)
            : Config(config)
            , Dir(dir)
            , Index(index)
            , IndexType(indexType)
            , UseGlobalFileMapping(useGlobalFileMapping)
            , IsReadOnly(isReadOnly)
            , ExecContext(execCtx)
        {}
    };

    struct TMergeContext {
        TRTYMerger::TContext Context;
        IMergerTask* Task;
        const ERealm Realm;
        const TString RealmName;

        IMerger* Merger = nullptr;
        const std::atomic<bool>* RigidStopSignal = nullptr;
        TString Identifier = "NOTSET";

        TMergeContext(const TRTYMerger::TContext& context, IMergerTask* task, ERealm realm, const TString& realmName)
            : Context(context)
            , Task(task)
            , Realm(realm)
            , RealmName(realmName)
        {}
    };

    struct TNormalizerContext {
        IIndexOwner* Index;
        IIndexManagersStorage& Managers;
        TPathName Dir;
        const TRTYServerConfig& Config;
        IIndexController::EIndexType IndexType;

        TNormalizerContext(IIndexOwner* index, const TPathName& dir, IIndexManagersStorage& managers, const TRTYServerConfig& config, IIndexController::EIndexType indexType)
            : Index(index)
            , Managers(managers)
            , Dir(dir)
            , Config(config)
            , IndexType(indexType)
        {}

        TString GetRealmName() const {
            TString realm = ToString(ERealm::Persistent);
            if (Index) {
                realm = Index->GetRealmName();
            } else {
                ERROR_LOG << "TNormalizerContext::Index == nullptr" << Endl;
            }
            INFO_LOG << "detect realm = " << realm << " for " << Dir.PathName() << Endl;
            return realm;
        }
    };

    struct TInfoChecker {
        ui32 Version;
        TString AdditionalHash;
        TInfoChecker() {
            Version = 1;
        }
    };

    struct IComponentParserContext {
        IComponentParserContext(TParsedDocument& result, const NRTYServer::TMessage::TDocument& document, NRTYServer::TMessage::TMessageType command, const NRTYServer::TDocParseContext& docParseContext)
            : Document(document)
            , Command(command)
            , Result(result)
            , DocParseContext(docParseContext)
        {}

        const NRTYServer::TMessage::TDocument& Document;
        NRTYServer::TMessage::TMessageType Command;
        TParsedDocument& Result;
        const NRTYServer::TDocParseContext& DocParseContext;
    };

    class IComponentParser: public TNonCopyable {
    public:
        typedef TAtomicSharedPtr<IComponentParser> TPtr;
        virtual void Parse(NRTYServer::IComponentParserContext& context) const = 0;
        virtual ~IComponentParser() {}
    };

    class IComponentInfo: public IStructuredInfo {
    public:
        using IStructuredInfo::IStructuredInfo;
    };

    class IIndexFilesGroup: public TNonCopyable {
    public:
        struct TIndexFile {
        public:
            enum EPrefetchPolicy { ppDisable, ppPrefetch, ppLock };
        public:
            TString Name;
            bool Checked;
            EPrefetchPolicy PrefetchPolicy;
            float PrefetchUpper;
            float PrefetchLower;
        public:
            TIndexFile(const TString& name, bool checked, EPrefetchPolicy prefetchPolicy)
                : Name(name)
                , Checked(checked)
                , PrefetchPolicy(prefetchPolicy)
                , PrefetchUpper(1.0f)
                , PrefetchLower(0.0f)
            {}

            bool operator<(const TIndexFile& other) const {
                return Name < other.Name;
            }
        };
        using TIndexFiles = TSet<TIndexFile>;

        public:
        virtual const TIndexFiles& GetIndexFiles() const = 0;
    };

    class IIndexComponent: public IIndexFilesGroup {
    public:
        struct TPriorityInfo {
            ui32 BuilderPriority;
            ui32 ManagerPriority;
            ui32 ComponentPriority;
            TPriorityInfo(const ui32 value) {
                BuilderPriority = ManagerPriority = ComponentPriority = value;
            }
        };

        typedef NObjectFactory::TParametrizedObjectFactory<IIndexComponent, TString, const TRTYServerConfig&> TFactory;
    protected:
        virtual bool DoMerge(const TMergeContext& context) const = 0;
        virtual bool DoAllRight(const TNormalizerContext& context) const = 0;
        virtual bool DoCheckMeta(const NRTYServer::TIndexMetadata& /*metaData*/) const { return true; };
        virtual bool DoMergeMeta(const TMergeContext& /*context*/) const { return true; };

    public:

        explicit IIndexComponent(bool isUsed);

        virtual ~IIndexComponent() {}

        virtual IComponentInfo::TPtr GetComponentInfo() const {return nullptr;}
        virtual IComponentParser::TPtr BuildParser() const = 0;
        virtual IParsedEntity::TPtr BuildParsedEntity(IParsedEntity::TConstructParams& params) const = 0;
        virtual bool CheckConfig() const { return true; }

        virtual bool IsCommonSearch() const {
            return true;
        }

        virtual bool Merge(const TMergeContext& context) const final;

        virtual bool AllRight(const TNormalizerContext& context) const final;

        virtual void CheckAndFix(const TNormalizerContext& context) const = 0;
        virtual bool GetInfoChecker(TInfoChecker& info) const { info.Version = 1; return true; }
        virtual bool CheckAlways() const { return false; }
        virtual TString GetName() const = 0;
        virtual TPriorityInfo GetPriority() const {
            return TPriorityInfo(DEFAULT_COMPONENT_PRIORITY);
        }
        virtual void CorrectSearchConfig(TSearchConfig& /*config*/, NRTYServer::TSearcherConfig::TSearchConfigType /*type*/) {}
        virtual bool TuneSearch(TBaseCommonSearch* /*search*/, IRemapperUrlDocId& /*remapper*/, TRTYIndexData* /*data*/) const { return false; }
        virtual THolder<IIndexComponentBuilder> CreateBuilder(const TBuilderConstructionContext& context) const = 0;
        virtual THolder<IIndexComponentManager> CreateManager(const TManagerConstructionContext& context) const = 0;
        virtual THolder<TPruningConfig::ICalcer> CreatePruningCalcer(const IIndexManagersStorage* /*managers*/) const {
            return {};
        }
        virtual void SearchCustom(const TVector<IIndexController::TPtr>& controllers, ICustomReportBuilder& builder, const TRTYSearchRequestContext& context) const;
    };
}
