#pragma once

#include <saas/library/daemon_base/metrics/metrics.h>
#include <saas/rtyserver/components/generator/builders_storage.h>
#include <saas/rtyserver/indexer_core/indexer_core.h>

#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/config/common_indexers_config.h>

#include <util/system/condvar.h>

class IIndexUpdater;
class IRealtimeSearchManager;
class TIndexUpdater;
class TRTYIndexData;
class TSearchRequestData;

class TMemoryIndexer: public TIndexerCore {
    friend class TMemoryIndexerUpdater;
private:
    struct TRTYMemDocInfo {
        TString TempDirName;
        TString Url;
        ui64 Kps;
        ui32 TempIndexDocId;
        bool IsCorrect;

    public:
        inline TRTYMemDocInfo()
            : IsCorrect(false)
        {}

        inline TRTYMemDocInfo(const TString& tempDirName, ui32 tempIndexDocId, const TString& url, ui64 kps)
            : TempDirName(tempDirName)
            , Url(url)
            , Kps(kps)
            , TempIndexDocId(tempIndexDocId)
            , IsCorrect(true)
        {}
    };

    class TDocsInfo {
    private:
        TDocsInfo()
            : Count(0)
        {}

    private:
        TAtomic Count;
        TVector<TRTYMemDocInfo> DocIds;

    public:
        explicit TDocsInfo(size_t size)
            : Count(0)
            , DocIds(size)
        {}

        size_t Size() const {
            return DocIds.size();
        }

        TVector<TRTYMemDocInfo>::const_iterator Begin() const {
            return DocIds.begin();
        }

        TVector<TRTYMemDocInfo>::const_iterator End() const {
            return DocIds.end();
        }

        const TRTYMemDocInfo& Get(ui32 docId) const {
            VERIFY_WITH_LOG(Size() > docId, "Incorrect docId");
            return DocIds[docId];
        }

        bool HasDoc(ui32 docId) const {
            VERIFY_WITH_LOG(Size() > docId, "Incorrect docId");
            return DocIds[docId].IsCorrect;
        }

        void BanDoc(ui32 docId) {
            VERIFY_WITH_LOG(Size() > docId, "Incorrect docId");
            VERIFY_WITH_LOG(DocIds[docId].IsCorrect, "docId %u already banned", docId);
            DocIds[docId].IsCorrect = false;
            AtomicDecrement(Count);
        }

        void AddDoc(ui32 docId, const TRTYMemDocInfo& info) {
            VERIFY_WITH_LOG(Size() > docId, "Incorrect docId");
            VERIFY_WITH_LOG(!DocIds[docId].IsCorrect, "docId %u already in use", docId);
            DocIds[docId] = info;
            AtomicIncrement(Count);
        }

        size_t GetCount() const {
            return AtomicGet(Count);
        }
    };
private:
    const NRTYServer::TSearcherConfig& SearcherConfig;

    TMutex MutexIndex;
    TCondVar CVWaitMutex;
    ITransaction TransactionClose;

    THolder<IRealtimeSearchManager> SearchManager;
    THolder<TRTYIndexData> IndexData;
    THolder<TIndexUpdater> Updater;
    const IDDKManager* DDKManager;
    TDocsInfo DocIds;

    TAutoGlobal<TQueriesMetrics> ConsumeMetrics;
    TAutoGlobal<TQueriesMetrics> WaitMetrics;
    TRTYIndexBuildersStorage Builders;

protected:
    void StartModification() override {
        MutexIndex.Acquire();
    }

    void FinishModification() override {
        CVWaitMutex.Signal();
        MutexIndex.Release();
    }

public:
    TMemoryIndexer(const TString& indexDirName, const TString& tempDirName, const TString& rootDir, const NRTYServer::TIndexerConfig& config, ui32 shard);
    ~TMemoryIndexer() noexcept;

    bool IsSearching() const override {
        return Config.Common.Owner.GetIndexerMemoryConfig().Enabled && Config.Common.Owner.GetSearcherConfig().Enabled;
    }

    ERequestType RequestType(const TCgiParameters& cgi) const override;
    TCommonSearch* GetCommonSearch() const override;

    ui32 GetDocumentsCount(bool keyWithDeleted = true) const override {
        Y_UNUSED(keyWithDeleted);
        return DocIds.GetCount();
    }

    ui32 GetSearchableDocumentsCount() const override {
        return GetDocumentsCount();
    }

    bool DecodeIntoTempDocId(const TDocSearchInfo& info, TDocSearchInfo& result) const override;
    bool RemapUrl2DocId(const TDocSearchInfo& docInfo, ui32& docId) const override;
    bool RemapUrl2DocIdCandidate(const TDocSearchInfo& docInfo, TDocIdCandidate& docId) const override;

    const NRTYServer::IIndexComponentManager* GetManager(const TString& componentName) const override {
        return Builders.GetManagers()->GetManager<NRTYServer::IIndexComponentManager>(componentName);
    }

    bool IsRemoved(const ui32 docId) const override {
        return !DocIds.HasDoc(docId);
    }

    EIndexType GetType() const override {
        return MEMORY;
    }
    NRTYServer::ERealm GetRealm() const override {
        return NRTYServer::ERealm::Realtime;
    }

    TString GetRealmName() const override {
        return "Realtime";
    }

    bool UpdateDoc(ui32 docId, const TParsedDocument::TPtr doc) override {
        CHECK_WITH_LOG(Builders.GetManagers()->IsOpened());
        return Builders.GetManagers()->UpdateDoc(docId, doc);
    }

    bool RestoreDoc(ui32 /*docId*/, TParsedDocument::TPtr& /*doc*/) override {
        return false;
    }

    bool UpdateTimestampsOnDelete(const TQueuedDocument& qDocument, int threadID) override;
    bool Index(const TQueuedDocument& qDocument, int /*threadID*/) override;
    TString Name() const override {
        return "MemoryIndexer";
    }
    const IDDKManager* GetDDKManager() const override {
        return DDKManager;
    }

    ui32 MarkDocIdsForDeleteUnsafe(const TVector<ui32>& /*docIds*/, ui32 /*marker*/) override {
        return 0;
    }

    ui32 DoRemoveDocIdsUnsafe(const TVector<ui32>& docIds) override;
    bool HasSearcher() const override {
        return true;
    }
    void CloseIndex(const std::atomic<bool>* rigidStopSignal, NRTYServer::EExecutionContext) override;
    bool GetDocInfo(const ui32 docId, NJson::TJsonValue& result) const override;
    void FillDocIds(TVector<ui32>& docIds) const override;
    ui32 RemoveKps(ui64 kps) override;
    IIndexUpdater* GetUpdater() override;
    ui64 GetFilesSize() const override; //IIndexController
    ui64 GetLockedMemorySize() const override; //IIndexController


private:
    bool RemoveDocIdNonLocked(const ui32 docId);
    bool RemoveUrlsFromTemp(const TString& tempDirName);
    void TraverseDocIds(std::function<void(ui32, const TRTYMemDocInfo&)>&& operation) const;

    bool Process(IMessage* message) override;
};
