#include "const.h"
#include "manager.h"
#include "searcher.h"

#include <saas/rtyserver/common/common_rty.h>
#include <saas/rtyserver/config/common_indexers_config.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/library/mapping/mapping.h>
#include <saas/rtyserver/key_inv_processor/ki_maker.h>
#include <saas/rtyserver/components/erf/erf_disk.h>
#include <saas/rtyserver/components/fullarchive/disk_manager.h>

#include <kernel/index_mapping/index_mapping.h>

#include <util/generic/cast.h>
#include <util/stream/mem.h>
#include <util/string/vector.h>

ui32 TSuggestIndexManager::FindSuggestInfo(const TString& text, const ui64 kps) const {
    ui64 minPos = 0;
    ui64 maxPos = ArcSort.Size();
    ui64 currPos;
    while (maxPos - minPos > 1) {
        currPos = 0.5 * (minPos + maxPos);
        TSuggestRecord sr = ArcSort.GetElem(currPos);
        if (sr.CompareWithKps(text, kps)) {
            minPos = currPos;
        } else {
            maxPos = currPos;
        }
        DEBUG_LOG << "Div iteration: " << text << "/" << sr.GetSuggest() << "/" << minPos << "/" << maxPos << Endl;
    }
    if (ArcSort.GetElem(minPos).GetSuggest().StartsWith(text))
        return minPos;
    return maxPos;
}

ui32 TSuggestIndexManager::DoRemoveDocids(const TVector<ui32>& docids) {
    ui32 result = 0;
    if (FullArc) {
        for (auto&& docid : docids) {
            TParsedDocument::TPtr doc = FullArc->GetDoc(docid);
            if (!doc)
                continue;
            ++result;
            DEBUG_LOG << "Document " << docid << "/" << doc->GetDocSearchInfo().GetUrl() << " removing from " << IndexDir << Endl;
            const TSuggestParsedEntity* entity = doc->GetComponentEntity<TSuggestParsedEntity>(ComponentName);
            const TInfoBySentence& info = entity->GetInfoBySentence();
            for (ui32 i = 0; i < info.CountSents(); ++i) {
                TString text = Strip(info.SentInfo(i).GetSuggest());
                if (!text)
                    continue;
                ui32 index = FindSuggestInfo(text, info.SentInfo(i).GetKps());
                if (ArcSort.Size() <= index)
                    continue;
                TSuggestRecord record = ArcSort.GetElem(index);
                CHECK_WITH_LOG(record.GetDocId() != TSuggestRecord::NoDocId);
                if (ToLowerUTF8(record.GetSuggest()) != ToLowerUTF8(text))
                    continue;
                TBasicFactorStorage data(ErfManager->GetFactorsInfo()->GetStaticFactorsCount()); // a block of ERF
                ErfManager->ReadRaw(data, record.GetDocId());
                TFactorStorage delta = info.SentInfo(i).GetFactors();
                VERIFY_WITH_LOG(delta.Size() <= data.Size(), "delta.Size() = %lu, data.Size() = %lu", delta.Size(), data.Size());
                for (ui32 i = 0; i < delta.Size(); ++i) {
                    ui32 oldData = BitCast<ui32>(data[i]);
                    ui32 deltaData = BitCast<ui32>(delta[i]);
                    if (oldData < deltaData) {
                        data[i] = 0;
                    } else {
                        ui32 factor = oldData - deltaData;
                        data[i] = BitCast<float>(factor);
                    }
                }
                ErfManager->Write(data, record.GetDocId());
            }
        }
    }
    return result;
}

TSuggestRecord TSuggestIndexManager::GetRecord(ui32 index) const {
    VERIFY_WITH_LOG(index < GetRecordsCount(), "index >= GetRecordsCount() !!!, %d, %d", index, GetRecordsCount());
    TSuggestRecord result = Arc.GetElem(index);
    TBasicFactorStorage erfBlock(ErfManager->GetFactorsInfo()->GetStaticFactorsCount());
    ErfManager->ReadRaw(erfBlock, index);
    result.LoadFactors(erfBlock);
    return result;
}

ui32 TSuggestIndexManager::GetDocumentsCountImpl() const {
    CHECK_WITH_LOG(!FullArc || FullArc->IsOpened());
    if (FullArc)
        return FullArc->GetDocumentsCount();
    CHECK_WITH_LOG(DDKManager);
    CHECK_WITH_LOG(DDKManager->IsOpened());
    return DDKManager->GetDocumentsCount();
}

bool TSuggestIndexManager::IsRemoved(ui32 docId) const {
    if (!FullArc)
        return false;
    return FullArc->IsRemoved(docId);
}

TSuggestIndexManager::TSuggestIndexManager(const NRTYServer::TManagerConstructionContext& context, const TString& componentName)
    : TBaseGeneratorManager(context.Config.Common.Owner, componentName)
    , SuggestConfig(Config.ComponentsConfig.Get<TSuggestComponentConfig>(ComponentName))
    , IndexDir(context.Dir.PathName())
    , FullArc(nullptr)
    , DDKManager(nullptr)
    , IsReadOnly(context.IsReadOnly)
{
    CHECK_WITH_LOG(SuggestConfig);
}

TSuggestIndexManager::~TSuggestIndexManager() {
    Close();
}

i64 TSuggestIndexManager::PrnValue(ui32 /*docid*/, i64 defaultValue) const {
    return defaultValue;
}

ERTYSearchResult TSuggestIndexManager::DoSearch(const TRTYSearchRequestContext& context, ICustomReportBuilder& reportBuilder, const IIndexController& /*controller*/) const {
    TSuggestSearcher searcher(*this, context.CgiParams(), reportBuilder);
    return searcher.Search();
}

bool TSuggestIndexManager::DoOpen() {
    if (!TBaseGeneratorManager::DoOpen()) {
        return false;
    }
    NOTICE_LOG << "Suggest manager opening for " << IndexDir << "..." << Endl;
    if (!Arc.Open(IndexDir, NRTYServer::NSuggest::ArchivePrefix)) {
        return false;
    }
    if (!ArcSort.Open(IndexDir, NRTYServer::NSuggest::SortedArchivePrefix)) {
        return false;
    }
    NOTICE_LOG << "Suggest manager opening for " << IndexDir << "... Arcs-OK" << Endl;

    TRTYErfDiskManager::TCreationContext cc(TPathName{IndexDir}, NRTYServer::NSuggest::ErfFileName, &FactorsDescription, IsReadOnly);
    ErfManager.Reset(new TRTYErfDiskManager(cc));
    if (!ErfManager->Open()) {
        return false;
    }
    NOTICE_LOG << "Suggest manager opening for " << IndexDir << "... Erfs-OK" << Endl;
    Reader.Reset(new TRTYKIReader(IndexDir, NRTYServer::NSuggest::KeyInvName));
    Reader->Open();
    NOTICE_LOG << "Suggest manager opening for " << IndexDir << "... KI-OK" << Endl;
    return Reader->IsOpen();
}

bool TSuggestIndexManager::DoClose() {
    if (!!Reader) {
        Reader->Close();
    }
    if (!!ErfManager) {
        ErfManager->Close();
    }
    Reader.Destroy();
    Arc.Close();
    ArcSort.Close();
    ErfManager.Destroy();
    return TBaseGeneratorManager::DoClose();
}

ui32 TSuggestIndexManager::GetDeletedDocumentsCount() const {
    if (FullArc) {
        CHECK_WITH_LOG(FullArc->IsOpened());
        return FullArc->GetDeletedDocumentsCount();
    } else {
        return 0;
    }
}

void TSuggestIndexManager::InitInteractions(const NRTYServer::IIndexManagersStorage& storage) {
    FullArc = storage.GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME);
    DDKManager = storage.GetManager<TRTYDDKManager>(DDK_COMPONENT_NAME);
}
