#include "builder.h"
#include "config.h"
#include "versions_normalizer.h"

#include <saas/rtyserver/components/fullarchive/disk_manager.h>

#include <robot/library/oxygen/base/protobuf/pb_utils.h>
#include <robot/library/oxygen/indexer/idspace/docid.h>
#include <robot/library/oxygen/indexer/processor/collection/collection.h>

const char* TProcessorVersionsNormalizer::Name() const {
    return "VersionsNormalizer";
}

bool TProcessorVersionsNormalizer::AllRight(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& /*indexFrq*/) const {
    const TFsPath directory = context.Dir.PathName();
    const NOxygen::TOxygenOptions* oxygenOptions = GetOxygenOptions(context);
    AssertCorrectConfig(oxygenOptions, "OxygenOptions are required for Versions normalizer");

    NOxygen::TGroupAttrMap attrMap;
    TProcessorsBuilderContext oxygenContext(*oxygenOptions);
    oxygenContext.SetForMerger(true).SetDir(directory).InitAttrs(attrMap);
    AssertCorrectConfig(TOXYIndexBuilder::BuildProcessors(oxygenContext), "cannot build processors");

    bool result = true;
    for (auto&& processor : oxygenContext.Processors) {
        const auto actual = processor->GetVersion();
        const auto current = processor->GetVersion(directory);
        if (actual != current) {
            WARNING_LOG << "Versions for processor " << processor->GetClassName() << " differ: actual " << actual << " current " << current << Endl;
            result = false;
        }
    }
    return result;
}

void TProcessorVersionsNormalizer::Fix(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& /*indexFrq*/) const {
    const auto archive = context.Managers.GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME);
    const TFsPath directory = context.Dir.PathName();
    const NOxygen::TOxygenOptions* oxygenOptions = GetOxygenOptions(context);
    const TRTYOxyConfig* config = Config.ComponentsConfig.Get<TRTYOxyConfig>(OXY_COMPONENT_NAME);
    AssertCorrectConfig(oxygenOptions, "OxygenOptions are required for Versions normalizer");
    AssertCorrectConfig(config, "OXY component config is not found");
    AssertCorrectConfig(archive, "FullArchive is required to restore Versions");
    AssertCorrectIndex(archive->IsOpened(), "FullArchive must be opened to restore Versions");

    NOxygen::TGroupAttrMap attrMap;
    TProcessorsBuilderContext oxygenContext(*oxygenOptions);
    oxygenContext.SetForMerger(true).SetDir(directory).InitAttrs(attrMap);
    AssertCorrectConfig(TOXYIndexBuilder::BuildProcessors(oxygenContext), "cannot build processors");

    TVector<NOxygen::TProcessorPtr> broken;
    for (auto&& processor : oxygenContext.Processors) {
        if (processor->GetVersion() != processor->GetVersion(directory)) {
            INFO_LOG << "Restoring " << processor->GetClassName() << " due to changed version" << Endl;
            broken.push_back(processor);
        }
    }
    CHECK_WITH_LOG(!broken.empty());

    TString layer;
    if (!layer) {
        const auto& layers = config->GetLayers().at(TRTYOxyConfig::FOR_MERGE);
        if (!layers.empty()) {
            layer = *layers.begin();
        }
    }
    if (!layer) {
        const auto& layers = config->GetLayers().at(TRTYOxyConfig::FOR_INDEX);
        if (!layers.empty()) {
            layer = *layers.begin();
        }
    }
    AssertCorrectConfig(!layer.empty(), "cannot determine proper layer for version normalizer");

    INFO_LOG << "Restoring versions in " << directory.Basename() << Endl;
    NOxygen::TProcessorCollection processor(broken);
    processor.Start();

    for (ui32 docId = 0; docId < archive->GetDocumentsCount(); ++docId) {
        if (archive->IsRemoved(docId)) {
            continue;
        }

        NRTYServer::TParsedDoc pd;
        CHECK_WITH_LOG(archive->ReadParsedDoc(layer, pd, docId));

        NOxygen::TObjectContext objectContext(pd.GetDocument().GetIndexedDoc().GetKiwiObject());
        NOxygen::TReturnObjectContext result = processor.Process(objectContext, docId).GetValue(TDuration::Max());
        AssertCorrectIndex(result.IsProcessedOk(), "Cannot restore Versions: %s", NOxygen::WritePbText(result.GetErrors()).data());
    }

    processor.Finish(nullptr);
    INFO_LOG << "Restored versions in " << directory.Basename() << Endl;
}
