#include "file_indexer.h"

#include <library/cpp/balloc/optional/operators.h>
#include <saas/rtyserver/indexer_core/index_metadata_processor.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/util/logging/exception_process.h>

TFileIndexer::TFileIndexer(const TString& directory, TIndexRepair& owner, TDocumentModifier& modifier)
    : Directory(directory)
    , FullArchive(directory, 0, owner.Config, 0, {}, false, false, false)
    , Owner(owner)
    , DocProcessed(TSystemEvent::rAuto)
    , IndexQueue("FileIndexer")
    , Modifier(modifier)
{}

void TFileIndexer::IndexDocument (const TQueuedDocument& qDoc) {
    TRY
        while (!qDoc->TryLockIndexer() && Owner.GetIsStarted()) {
            Sleep(TDuration::MilliSeconds(200));
        }
        if (!Owner.GetIsStarted()) {
            ERROR_LOG << "Repairing interrupted" << Endl;
            Result = false;
            return;
        }
        DEBUG_LOG << qDoc->GetDocSearchInfo().GetUrl() << " read from fullarchive. Not marked for delete" << Endl;
        ++CountPushedDocuments;
        if (!Indexer) {
            TString indexDirName;
            int shard = qDoc->GetDocument().GetShard();
            Owner.IndexStorage.AllocateIndex(indexDirName, TempDirName, shard, /*isDiskIndex=*/ true, /*isPrepIndex=*/ false);
            Indexer.Reset(Owner.DiskIndexManager.GetDiskIndexer(indexDirName, TempDirName, shard));
        }
        VERIFY_WITH_LOG(qDoc->GetCommand() == NRTYServer::TMessage::ADD_DOCUMENT, "document %s has type %i, MODIFY_DOCUMENT expected", qDoc->GetDocSearchInfo().GetUrl().data(), (int)qDoc->GetCommand());

        TModifyActionVersionedDelete deleteAction(qDoc);
        TModifyResult modified = Modifier.ModifyDocument(qDoc, deleteAction);
        if (modified.Status != TModifyResult::OUTDATED) {
            if (!Indexer->Index(qDoc, 0)) {
                ERROR_LOG << qDoc->GetDocument().GetErrorsCollector().GetStringReport() << Endl;
                Result = false;
                return;
            };
        }
        if (Indexer->IsFull())
            FullIndexers.push_back(Indexer);
        ++CountRepairedDocuments;
    CATCH ("While repair doc " + qDoc->GetDocSearchInfo().GetUrl() + " in " + Directory)
}

bool TFileIndexer::Run() {
    TRY
        Result = true;
        CountRepairedDocuments = 0;
        CountPushedDocuments = 0;
        if (!FullArchive.Open()) {
            VERIFY_WITH_LOG(FullArchive.Repair(), "cannot repair full archive manager in %s", Directory.data());
            VERIFY_WITH_LOG(FullArchive.Open(), "cannot start full archive manager in %s", Directory.data());
        }
        {
            IndexQueue.Start(1);
            TRY
                for (TDiskFAManager::TIterator::TPtr iter = FullArchive.CreateIterator(); iter->IsValid(); iter->Next()) {
                    TQueuedDocument qDoc = new TGuardedDocument(iter->GetDocument());
                    DEBUG_LOG << iter->GetDocument()->GetDocSearchInfo().GetUrl() << " read from fullarchive (" << qDoc->GetDocumentHash() << "/" << iter->GetDocId() << ")" << Endl;
                    INamedLockManager::StartLock(qDoc->GetDocumentHash(), new TLockCallback(*this, qDoc));
                    DocProcessed.Wait();
                }
            CATCH("While repair");
            IndexQueue.Stop();
            if (!!Indexer)
                FullIndexers.push_back(Indexer);
            if (!FullIndexers.empty()) {
                const TFsPath normalIndex(TFsPath(Directory) / "normal_index");
                if (Owner.GetIsStarted())
                    normalIndex.RenameTo(TFsPath(Owner.Config.GetRealmListConfig().GetMainRealmConfig().RealmDirectory) / TempDirName / "normal_index");
            }
            while (!FullIndexers.empty()) {
                FullIndexers.front()->CloseIndex(Owner.GetRigidStopSignal(), NRTYServer::EExecutionContext::Repair);
                FullIndexers.pop_front();
            }
        }
        VERIFY_WITH_LOG(FullArchive.Close(), "Cannot stop full archive manager in %s", Directory.data());
        INFO_LOG << CountRepairedDocuments << " from " << CountPushedDocuments << " documents were restored" << Endl;
        return Result;
    CATCH_AND_RETHROW("while indexing from " + Directory + " to TFileIndexer ")
}

TFileIndexer::TLockCallback::TLockCallback(TFileIndexer& owner, const TQueuedDocument& qDoc)
    : Owner(owner)
    , QDoc(qDoc)
{}

void TFileIndexer::TLockCallback::OnLock(INamedLockManager::TNamedLockPtr lock) {
    QDoc->StoreLock(lock);
    CHECK_WITH_LOG(Owner.IndexQueue.Add(new TIndexerTask(Owner, QDoc)));
}

TFileIndexer::TIndexerTask::TIndexerTask(TFileIndexer& owner, const TQueuedDocument& qDoc)
    : Owner(owner)
    , QDoc(qDoc)
{}

void TFileIndexer::TIndexerTask::Process(void* /*ThreadSpecificResource*/) {
    ThreadDisableBalloc();

    THolder<TIndexerTask> suicide(this);
    Owner.IndexDocument(QDoc);
}

TFileIndexer::TIndexerTask::~TIndexerTask() {
    Owner.DocProcessed.Signal();
}
