#pragma once

#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/rtyserver/final_index/final_index.h>
#include <saas/rtyserver/indexer_core/merger_interfaces.h>

class TFinalIndexDeferredStartVerifier {
public:
    inline TFinalIndexDeferredStartVerifier(TFinalIndexPtr finalIndex)
        : FinalIndex(finalIndex)
    {
    }

    inline ~TFinalIndexDeferredStartVerifier() {
        VERIFY_WITH_LOG(!FinalIndex || FinalIndex->GetIsStarted(), "Final index was added to IndexStorage but not started")
    }

    inline operator TFinalIndexPtr() const {
        return FinalIndex;
    }

    TFinalIndexPtr Get() {
        return FinalIndex;
    }

private:
    TFinalIndexPtr FinalIndex;
};

class TFinalIndexGuardedPtr : public TAtomicSharedPtr<TFinalIndexDeferredStartVerifier> {
public:
    inline TFinalIndexGuardedPtr()
    {}

    inline TFinalIndexGuardedPtr(TFinalIndexPtr finalIndex)
        : TAtomicSharedPtr<TFinalIndexDeferredStartVerifier>(new TFinalIndexDeferredStartVerifier(finalIndex))
    {}
};

class TIndexStorageConstructionContext {
private:
    TString IndexDirectory;
    TString SpecificPrefix;
    ui32 SearchersCountLimit;
public:
    const TRTYServerConfig& Config;
    TIndexStorageConstructionContext(const TRTYServerConfig& config, const TString& indexDirectory = "", const TString& specificPrefix = "")
        : Config(config)
    {
        SearchersCountLimit = Config.SearchersCountLimit;
        if (!!specificPrefix) {
            SearchersCountLimit = 1;
        }
        IndexDirectory = TFsPath(!!indexDirectory ? indexDirectory : Config.GetRealmListConfig().GetMainRealmConfig().RealmDirectory).Fix().GetPath() + "/";
        SpecificPrefix = specificPrefix;
    }

    const TString& GetIndexDirectory() const {
        return IndexDirectory;
    }

    const TString& GetSpecificPrefix() const {
        return SpecificPrefix;
    }

    ui32 GetSearchersCountLimit() const {
        return SearchersCountLimit;
    }
};

class TIndexStorage : public IIndexStorage, public TNonCopyable, public TThrRefBase {
public:
    using TPtr = TIntrusivePtr<TIndexStorage>;
private:
    mutable TRWMutex ReadWriteMutex;
    THashMap<TString, ui32> RealmIndexes;
    THashSet<TString> ExceededLimitRealms;

    i32 SegmentId;
    TMap<TString, TFinalIndexPtr> FinalIndexes;
    const TRTYServerConfig& Config;
    const TIndexStorageConstructionContext Context;
    bool NoSearchableGlobal;
    bool CanIgnoreIndexOrigin = false;

private:
    TIndexStorage(const TIndexStorageConstructionContext& context);

public:
    static TIndexStorage::TPtr Create(const TIndexStorageConstructionContext& context) {
        return new TIndexStorage(context);
    }

    virtual ~TIndexStorage();

    void Init();
    void Stop();

    const TIndexStorageConstructionContext& GetContext() const {
        return Context;
    }

    void StopSearching() {
        TReadGuard g(ReadWriteMutex);
        UnregisterSearchers(GetFinalIndexes(""));
        NoSearchableGlobal = true;
    }

    ui32 GetSearchersCount(ui32 shard) const;

    virtual bool IsFinalIndex(const TString& indexDirOrName) const override;
    virtual bool IsEmptyIndex(const TString& indexDirOrName) const override;
    virtual bool HasIndexFiles(const TString& indexDirOrName) const override;
    virtual ui32 GetDocsCount(const TString& dirName, bool withDeleted) const override;

    virtual void AllocateIndex(TString& indexDirName, TString& tempDirName, int shard, bool isDiskIndex, bool isPrepIndex = false) override;
    virtual void AllocateIndexUnsafe(TString& indexDirName, TString& tempDirName, int shard, bool isDiskIndex, bool isPrepIndex = false) override;
    virtual bool PrepareIndexForConsumption(const TString& indexDirName, bool storeInIndexRoot, TString& finalIndex) override;
    virtual TIndexControllerPtr ConsumeIndex(const TString& indexDirName, NRTYServer::EConsumeMode mode) override;
    virtual TIndexControllerPtr ConsumeAllocatedIndex(const TString& finalIndex, NRTYServer::EConsumeMode mode) override;
    virtual THolder<IIndexConsumeTransaction> StartIndexConsumeTransaction() override;

    TFinalIndexGuardedPtr AddIndex(const TString& pindexDir, const TString& originDir, bool deferredStart, TVector<ui32>* decoder, NRTYServer::EExecutionContext execCtx, bool acquireLock = true);
    virtual TDeferredRemoveTaskPtr RemoveIndexes(const TVector<TString>& indexDirs, bool ignoreIndexOrigin) override;
    void UnregisterSearchers(const TVector<TString>& sources);

    TFinalIndexPtr GetFinalIndex(const TString& dir) const;
    TFinalIndexPtr GetFinalIndexUnsafe(const TString& dir) const;
    virtual TVector<TString> GetFinalIndexes(const TString& path) const override;
    virtual TVector<TString> GetFinalIndexesUnsafe(const TString& path) const override;
    ui64 GetFinalIndexesCount() const;
    bool ExceededNonMergedIndexesCount() const;
    virtual TString GetFinalIndexDirs(bool fullPath) const override;
    virtual TString GetFullPath(const TString& indexDirOrName, const TString& path = "") const override;
    virtual TIndexControllerPtr GetIndexController(const TString& dir) const override {
        return GetFinalIndex(dir);
    }
};
