#include "documents_queue.h"
#include "indexer_server.h"

#include <saas/library/behaviour/behaviour.h>
#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/common/message_collect_server_info.h>
#include <saas/rtyserver/indexer_core/parsed_document.h>
#include <saas/rtyserver/logging/rty_index.h>
#include <saas/rtyserver/config/indexer_config.h>

TSynchronizedDocumentsQueue::TSynchronizedDocumentsQueue(TIndexerServer& indexerServer)
    : IndexerConfig(indexerServer.Config)
    , ServiceName(indexerServer.GetServiceName())
    , IsActive(true)
{
    RegisterGlobalMessageProcessor(this);
}

bool TSynchronizedDocumentsQueue::Process(IMessage* message) {
    TMessageCollectServerInfo* messInfo = dynamic_cast<TMessageCollectServerInfo*>(message);
    if (messInfo) {
        if (IsActive) {
            if (IndexerConfig.GetType() == "disk")
                messInfo->AddDiskQueueSize(DocumentsQueue.Size());
            if (IndexerConfig.GetType() == "memory")
                messInfo->AddMemoryQueueSize(DocumentsQueue.Size());
        }
        return true;
    }

    return false;
}

void TSynchronizedDocumentsQueue::Put(TQueuedDocument& document, bool forcePut) {
    // nail down document url to close the race between logging and TIndexersThread::ProcessUpdate document modification
    const TString url = document->GetDocument().GetDocSearchInfo().GetUrl();
    const ui64 kps = document->GetDocument().GetDocSearchInfo().GetKeyPrefix();

    if (IsActive && (DocumentsQueue.Size() < IndexerConfig.DocumentsQueueSize || forcePut)) {
        const TDeferredRepliesProcessor::TDeferredReplyCheck res = CheckDeferredReply(document.Get(), ServiceName);
        if (res == TDeferredRepliesProcessor::drcStartNew)
            document->UnLock();
        if (res == TDeferredRepliesProcessor::drcNoNeed || res == TDeferredRepliesProcessor::drcStartNew) {
            DocumentsQueue.Put(document, true);
            DEBUG_LOG << "Adding document id=" << document->GetMessageId() << ";url=" << url << " to queue" << Endl;
            DocumentLog(ServiceName, url, kps, IndexerConfig.GetTypeWithPort(), "PUT");
        } else {
            DEBUG_LOG << "REPLIED document id=" << document->GetMessageId() << ";url=" << url << Endl;
            DocumentLog(ServiceName, url, kps, IndexerConfig.GetTypeWithPort(), "REPLIED");
        }
    } else {
        DEBUG_LOG << "Reject document id=" << document->GetMessageId() << ";url=" << url << Endl;
        DocumentLog(ServiceName, url, kps, IndexerConfig.GetTypeWithPort(), "REJECTED");
        document->SetStatus(NRTYServer::TReply::NOTNOW, "TSynchronizedDocumentsQueue is not active or queue is full");
    }
}

TSynchronizedDocumentsQueue::~TSynchronizedDocumentsQueue() {
    UnregisterGlobalMessageProcessor(this);
}

void TSynchronizedDocumentsQueue::Stop() {
    IsActive = false;
    while (!DocumentsQueue.IsEmpty()) {
        Sleep(TDuration::MilliSeconds(300));
    }
}

void TSynchronizedDocumentsQueue::LockAndPut(TQueuedDocument& document, bool forcePut) {
    if (document->IsLocked()) {
        DEBUG_LOG << "action=locked_put;id=" << document->GetMessageId() << ";forcePut=" << forcePut << Endl;
        Put(document, forcePut);
        DEBUG_LOG << "action=locked_put_done;id=" << document->GetMessageId() << ";forcePut=" << forcePut << Endl;
    } else {
        DEBUG_LOG << "action=startlock_put;hash=" << document->GetDocumentHash() << ";id=" << document->GetMessageId() << ";forcePut=" << forcePut << Endl;
        INamedLockManager::StartLock(document->GetDocumentHash(), new TPuter(document, forcePut, *this));
        DEBUG_LOG << "action=startlock_put_done;id=" << document->GetMessageId() << ";forcePut=" << forcePut << Endl;
    }
}

TSynchronizedDocumentsQueue::TPuter::TPuter(TQueuedDocument document, bool forcePut, TSynchronizedDocumentsQueue& queue)
    : Document(document)
    , ForcePut(forcePut)
    , Queue(queue)
{}

void TSynchronizedDocumentsQueue::TPuter::OnLock(INamedLockManager::TNamedLockPtr lock) {
    Document->StoreLock(lock);
    Queue.Put(Document, ForcePut);
}
