#include "memory_search.h"

#include <saas/rtyserver/search/base/search.h>
#include <saas/rtyserver/search/external_search/rty_index_data.h>

#include <saas/library/daemon_base/metrics/metrics.h>

#include <search/memory/indexeddoc/idconsumer.h>
#include <search/memory/search.h>
#include <search/memory/yndex.h>

class TMemorySearchManager::TReadContext : public IRealtimeSearchManager::IReadContext {
private:
    THolder<NMemorySearch::TMemoryYndexReader> Reader;
    THolder<TYndexRequester> Requester;
public:

    TReadContext(NMemorySearch::TMemorySearch* memorySearch) {
        Reader.Reset(memorySearch->CreateYndexReader());
        Requester.Reset(Reader->CreateTextRequester(memorySearch->GetRequesterData()));
    }

    const IKeysAndPositions* Get() const override {
        return &Requester->YMain();
    }
};

TMemorySearchManager::TMemorySearchManager(IIndexController::TPtr contoller, const NRTYServer::TSearcherConfig& searcherConfig)
    : SearcherConfig(searcherConfig)
    , Shard(contoller->GetShard())
{
    Y_UNUSED(SearcherConfig);
    NMemorySearch::TMemorySearchOptions options;
    options.Metrics = &GetGlobalMetrics();
    options.MetricPrefix = GetMetricsPrefix() + "MemorySearch" + (Shard ? ToString(Shard) : Default<TString>());
    MemorySearch = NRTYServer::CreateMemorySearch(contoller, options);
}

TMemorySearchManager::~TMemorySearchManager() {
    MemorySearch.Destroy();
}

const NGroupingAttrs::TDocsAttrs* TMemorySearchManager::GetDocsAttrs() const {
    return MemorySearch->GetYndex()->GetIndexStaticData().GetDocsAttrs();
}

const IRealtimeSearchManager::IReadContext::TPtr TMemorySearchManager::GetKeysAndPositions() const {
    return new TReadContext(MemorySearch.Get());
}

void TMemorySearchManager::BanDoc(const ui32 docId) {
    CHECK_WITH_LOG(!!MemorySearch);
    MemorySearch->GetYndex()->BanDoc(docId);
}

bool TMemorySearchManager::HasDoc(const TString& url, ui32& docId) const {
    CHECK_WITH_LOG(!!MemorySearch);
    return MemorySearch->GetYndex()->HasDoc(url, docId);
}

void TMemorySearchManager::SignalSearchingStart() {
    CHECK_WITH_LOG(!!MemorySearch);
    MemorySearch->SignalSearchingStart();
}

void TMemorySearchManager::SearchClose() {
    CHECK_WITH_LOG(!!MemorySearch);
    MemorySearch->SearchClose();
}

void TMemorySearchManager::SearchOpen(TAutoPtr<TSearchConfig> config, const NRTYServer::TIndexerConfig& /*indexerConfig*/, TRTYIndexData* indexData) {
    CHECK_WITH_LOG(indexData);
    CHECK_WITH_LOG(!!MemorySearch);
    MemorySearch->SearchOpen(config, nullptr);
    MemoryYndexWriter = MemorySearch->GetYndexWriter();
    indexData->SetDocsAttrs(MemorySearch->GetYndex()->GetIndexStaticData().GetDocsAttrs());
}

const IArchiveData* TMemorySearchManager::GetArchiveData() const {
    CHECK_WITH_LOG(!!MemorySearch);
    return MemorySearch->GetArchiveData();
}

TBaseCommonSearch* TMemorySearchManager::GetCommonSearch() {
    return MemorySearch.Get();
}

ERequestType TMemorySearchManager::RequestType(const TCgiParameters& cgi) const {
    CHECK_WITH_LOG(!!MemorySearch);
    return MemorySearch->RequestType(cgi);
}

NMemorySearch::EConsumeDataResult TMemorySearchManager::Consume(NRealTime::TIndexedDoc* indexedDoc, const TDocSearchInfo& docInfo, TCondVar& cvWaitMutex, TMutex& mutexIndex, TQueriesMetrics& consumeMetrics) {
    VERIFY_WITH_LOG(indexedDoc, "Incorrect usage memory indexer");
    const TString originalUrl = indexedDoc->GetUrl();
    indexedDoc->SetUrl(docInfo.GetMSHash());

    NMemorySearch::EConsumeDataResult dr;
    TInstant start = Now();
    TAtomicSharedPtr<TSystemEvent> consumed = NMemorySearch::TIndexedDocInfo::ConsumeAsyncIndexedDocInfo(
        /*consumer=*/ *MemoryYndexWriter,
        /*indexedDoc=*/ *indexedDoc,
        /*docTime=*/ TInstant(),
        /*enqueueTime=*/ TInstant::Now(),
        /*consumeResult=*/ dr
    );
    for (size_t waitCount = 0; !consumed->WaitT(TDuration::MilliSeconds(100)); ++waitCount) {
        if (waitCount % 100 == 0) {
            DEBUG_LOG << "Memory indexer consume waiting (waited " << waitCount << " times)" << Endl;
        }
        cvWaitMutex.WaitT(mutexIndex, TDuration::MilliSeconds(1));
    }
    const TDuration elapsed = Now() - start;
    consumeMetrics.Hit(elapsed.MicroSeconds());
    DEBUG_LOG << "Memory indexer consume waited " << elapsed.MilliSeconds() << " ms" << Endl;

    indexedDoc->SetUrl(originalUrl);

    return dr;
}

ui32 TMemorySearchManager::GetDocLen(ui32 docid) const {
    return MemorySearch->GetErfManager()->GetDocLen(docid);
}
