#include "yndex_normalizer.h"
#include "index_component.h"
#include "index_manager.h"

#include <saas/rtyserver/merger/doc_extractor.h>
#include <saas/rtyserver/merger/merger_action.h>
#include <saas/rtyserver/merger/m2n_decoder.h>
#include <saas/rtyserver/components/generator/manager.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/indexer_core/index_component_storage.h>
#include <saas/util/logging/exception_process.h>

#include <kernel/keyinv/indexfile/oldindexfile.h>

TYndexNormalizer::TYndexNormalizer(const TRTYServerConfig& config)
    : TIndexFrqNormalizer(config)
{}

const char* TYndexNormalizer::Name() const {
    return "docids order";
}

bool TYndexNormalizer::AllRight(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& /*indexFrq*/) const {
    const bool checkForPruningChange = !context.Config.IsReadOnly;
    const TRTYIndexManager* manager = context.Managers.GetManager<TRTYIndexManager>(INDEX_COMPONENT_NAME);
    THolder<TPruningConfig::ICalcer> calcer(TIndexComponentsStorage::Instance().CreatePruningCalcer(&context.Managers));
    i64 prevDocId = -1;
    double prevValue = Max<double>();
    for (TPosIterator<> iter(*context.Index->GetYndex(), YDX_DOC_LEN_KEY, RH_DEFAULT); iter.Valid(); iter.Next()) {
        if (iter.Doc() - prevDocId != 1) {
            NOTICE_LOG << "docids order check failed: " << prevDocId << "," << iter.Doc() << Endl;
            return false;
        }
        if (checkForPruningChange && !manager->IsRemoved(iter.Doc())) {
            double value = calcer->PruningRank(iter.Doc());
            if (value > prevValue) {
                NOTICE_LOG << "docids order check failed (pruningRank): " << value << "(" << iter.Doc() << ") > " << prevValue << "(" << prevDocId << ")" << Endl;
                return false;
            }
            prevValue = value;
        }
        prevDocId = iter.Doc();
    }
    return true;
}

void TYndexNormalizer::Fix(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& indexFrq) const {
    TRY
        INFO_LOG << "Try to repair doc ids for " << context.Dir.BaseName() << Endl;
        const TRTYIndexManager* manager = context.Managers.GetManager<TRTYIndexManager>(INDEX_COMPONENT_NAME);
        ui32 docCount = manager->GetDocumentsCount();
        THolder<TPruningConfig::ICalcer> calcer(TIndexComponentsStorage::Instance().CreatePruningCalcer(&context.Managers));

        TM2NDecoder::TOptions options;
        options.SegmentSize = docCount;
        options.SizeDeviation = 1;
        options.MaxDeadlineDocs = 0;
        options.Pruning = true;
        TM2NDecoder decoder(options);
        decoder.AddInfo(nullptr);
        for (TPosIterator<> iter(*context.Index->GetYndex(), YDX_DOC_LEN_KEY, RH_DEFAULT); iter.Valid(); iter.Next())
            decoder.AddInfo(iter.Doc(), manager->IsRemoved(iter.Doc()) ? -1 : 0, TDocPlaceInfo(iter.Doc(), calcer->PruningRank(iter.Doc())), 0);
        decoder.Finalize();
        {
            NRTYServer::IIndexOwner::TGuardIndexModification g(context.Index);
            TVector<TString> sources(1, context.Dir.PathName());
            TFsPath fullIndexDir(context.Dir.PathName());
            TFsPath tempResult(fullIndexDir.Parent());
            tempResult = tempResult / "normalizer" / context.Dir.BaseName();
            NOTICE_LOG << "Try to merge " << context.Dir.BaseName() << Endl;
            TMergerAction mergerAction(Config, TMergerEngine::GetMerger(), fullIndexDir.c_str());
            mergerAction.Do(sources, tempResult.GetPath(), &decoder, nullptr, nullptr, nullptr, nullptr);
            if ((fullIndexDir / "timestamp").Exists())
                TFsPath(fullIndexDir / "timestamp").RenameTo(tempResult / "timestamp");
            fullIndexDir.ForceDelete();
            INFO_LOG << "Try to rename " << tempResult << " to " << fullIndexDir << Endl;
            tempResult.RenameTo(fullIndexDir);
        }
        if (!TIndexFrqNormalizer::AllRight(context, indexFrq))
            TIndexFrqNormalizer::Fix(context, indexFrq);
        INFO_LOG << "doc ids repair finished for " << context.Dir.BaseName() << Endl;
        return;
    CATCH("on RepairDocIds");
    VERIFY_WITH_LOG(false, "Normalization failed: RepairDocIds");
}
