#include "normalizer.h"
#include "fb_writer.h"
#include "core.h"

#include <saas/rtyserver/components/fullarchive/disk_manager.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/indexer_core/index_metadata_processor.h>

#include <kernel/tarc/docdescr/docdescr.h>
#include <kernel/tarc/disk/searcharc.h>

#include <util/system/compiler.h>

namespace NRTYServer {
    namespace {
        struct TKeyValueCollector final : public IDocInfosBuilderCallback {
            explicit TKeyValueCollector(const TPropConfig& config)
                : Config(config)
            {}

            void OnBuildProperty(const char* name, const char* value) override {
                if (Config.ShouldContainProperty(name)) {
                    KeyValue.emplace_back(name, value);
                }
            }

            const TPropConfig& Config;
            TVector<std::pair<TStringBuf, TStringBuf>> KeyValue;
        };
    }

    TPropNormalizer::TPropNormalizer(const TString&, IL2ComponentCore::TPtr core, const TRTYServerConfig& config, const TL2DocStorageParams&)
        : NRTYServer::INormalizer(config)
        , Config(dynamic_cast<TPropCore&>(*core).GetConfig())
    {}

    const char* TPropNormalizer::Name() const {
        return PropComponentName.data();
    }

    bool TPropNormalizer::AllRight(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& /* indexFrq */) const {
        TIndexMetadataProcessor indexMetadata(context.Dir.PathName());
        const auto before = indexMetadata->GetPropConfig();
        const auto after = Config->PropertyNamesToString();
        if (before == after) {
            return true;
        } else {
            INFO_LOG << "The list of properties for PROP component changed from '" << before << "' to '" << after << "'\n";
            return false;
        }
    }

    void TPropNormalizer::Fix(const NRTYServer::TNormalizerContext& context, const THolder<TFileMap>& /* indexFrq */) const {
        INFO_LOG << "Running normalizer for PROP\n";
        if (Config->GetEnableNormalizer()) {
            auto* fullArc = context.Managers.GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME);

            size_t count = 0;
            if (fullArc->DoesLayerExist("full")) {
                INFO_LOG << "Getting properties from full layer\n";
                count = FixFromFullarc(context);
            } else {
                INFO_LOG << "Getting properties from tarc\n";
                count = FixFromTarc(context);
            }

            INFO_LOG << "Successfully reindexed " << count << " documents in PROP layer\n";
        } else {
            INFO_LOG << "PROP normalizer is disabled; doing nothing\n";
        }

        TIndexMetadataProcessor indexMetadata(context.Dir.PathName());
        indexMetadata->SetPropConfig(Config->PropertyNamesToString());
        indexMetadata.Flush();
    }

    size_t TPropNormalizer::FixFromFullarc(const NRTYServer::TNormalizerContext& context) const {
        // We need to change data
        TDiskFAManager* fullArc = const_cast<TDiskFAManager*>(context.Managers.GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME));
        VERIFY_WITH_LOG(fullArc && fullArc->IsOpened(), "Fullarc must be opened");

        TString name{PropComponentName};
        size_t count = 0;
        for (auto documentIterator = fullArc->CreateIterator("full"); documentIterator->IsValid(); documentIterator->Next()) {
            fullArc->IndexLayerUnsafe(
                name,
                WriteDocumentFlatbuffer(documentIterator->GetParsedDoc().GetDocument(), *Config),
                documentIterator->GetDocId());
            ++count;
        }

        return count;
    }

    size_t TPropNormalizer::FixFromTarc(const NRTYServer::TNormalizerContext& context) const {
        TDiskFAManager* fullArc = const_cast<TDiskFAManager*>(context.Managers.GetManager<TDiskFAManager>(FULL_ARCHIVE_COMPONENT_NAME));
        VERIFY_WITH_LOG(fullArc && fullArc->IsOpened(), "Fullarc must be opened");

        TSearchArchive archive;
        archive.Open(context.Dir.PathName() + "/index", AOM_MAP, context.Config.GetSearcherConfig().ArchiveType);
        Y_ENSURE(archive.IsOpen(), "Cannot open tarc");

        TString name{PropComponentName};
        size_t count = 0;
        for (const auto docId: xrange<ui32>(archive.GetMaxHndl() + 1)) {
            if (!archive.IsInArchive(docId)) {
                continue;
            }
            const auto extInfo = archive.GetExtInfo(docId);
            TDocDescr docDescr;
            docDescr.UseBlob(extInfo.Data(), extInfo.Size());
            TKeyValueCollector collector{*Config};
            docDescr.ConfigureDocInfos(&collector);

            fullArc->IndexLayerUnsafe(
                name,
                WriteDocumentFlatbuffer(std::move(collector.KeyValue)),
                docId);
            ++count;
        }

        return count;
    }
} // namespace NRTYServer
