#include "component.h"
#include "builder.h"
#include "manager.h"
#include "index.h"

#include <search/base/yx_search.h>
#include <saas/rtyserver/search/factory/factory.h>

#include <saas/rtyserver/config/searcher_config.h>

#include <util/generic/ptr.h>

namespace {
    const TIndexFileEntry LockedIndexFiles[] = {
        { "indexinvhash", 0.0f, 1.0f },
        { "indexinv", 0.0f, 1.0f },
        { "indexkey", 0.0f, 1.0f },
        { "indexaa", 0.0f, 1.0f }
    };

    const TIndexFileEntry ExtraLockedIndexFiles[] = {
        { "indexarc", 0.0f, 1.0f },
        { "indexdir", 0.0f, 1.0f },
    };

}

TBaseIndexComponent::TBaseIndexComponent(const TRTYServerConfig& config, bool isUsed)
    : IIndexComponent(isUsed)
    , Config(config)
    , ExternalSearchCreator(NRTYServer::IExternalSearchCreator::TFactory::Construct(config.GetSearcherConfig().ExternalSearch))
{
    RegisterIndexFilesList(LockedIndexFiles, TIndexFile::ppLock);

    if (config.GetSearcherConfig().ArchivePolicy == ARCHIVE_POLICY_MAPMEMLOCK) {
        RegisterIndexFilesList(ExtraLockedIndexFiles, TIndexFile::ppLock);
    }

    Y_VERIFY(!Config.GetSearcherConfig().DefaultConfigInstances.empty());
    const auto& baseConfig = Config.GetSearcherConfig().DefaultConfigInstances[NRTYServer::TSearcherConfig::sctBase];
    if (baseConfig) {
        const auto& listToLock = baseConfig->LockIndexFiles;
        if (listToLock) {
            RegisterIndexFilesList(*listToLock, TIndexFile::ppLock);
        }

        const auto& listToPrefetch = baseConfig->PrefetchIndexFiles;
        if (listToPrefetch.Get() != nullptr) {
            RegisterIndexFilesList(*listToPrefetch, TIndexFile::ppPrefetch);
        }
    }

    if (ExternalSearchCreator) {
        RegisterIndexFilesList(ExternalSearchCreator->GetLockedIndexFiles(), TIndexFile::ppLock);
        RegisterIndexFilesList(ExternalSearchCreator->GetPrefetchedIndexFiles(), TIndexFile::ppPrefetch);
    }
}

TBaseIndexComponent::~TBaseIndexComponent() = default;

bool TBaseIndexComponent::CheckConfig() const {
    const TString& externalSearch = Config.GetSearcherConfig().ExternalSearch;
    if (!externalSearch.empty() &&
        !NRTYServer::IExternalSearchCreator::TFactory::Has(externalSearch))
    {
        ERROR_LOG << "External search " << externalSearch << " is not registered" << Endl;
        return false;
    }

    return true;
}

const NRTYServer::IIndexComponent::TIndexFiles& TBaseIndexComponent::GetIndexFiles() const {
    return IndexFiles;
}

bool TBaseIndexComponent::IsFinalIndex(const TString& path) const {
    return NRTYServer::HasTextWad(path) || NRTYServer::HasYndex(path);
}

class TGrAttrPruningCalcer : public TPruningConfig::ICalcer {
public:
    TGrAttrPruningCalcer(const TBaseGeneratorManager* manager, const TString& attrName)
        : Manager(manager)
        , AttrName(attrName)
    {}

    double PruningRank(ui32 docid) const override {
        VERIFY_WITH_LOG(Manager, "invlid usage");
        return static_cast<double>(Manager->GroupAttrValue(docid, AttrName.data(), 0));
    }

    double PruningRankByDoc(const TPruningConfig::IDocument* document) const override {
        i64 result;
        if (!document->GetGroupAttrValue(AttrName, result))
            result = Min<i64>();
        return static_cast<double>(result);
    }

private:
    const TBaseGeneratorManager* Manager;
    TString AttrName;
};

THolder<TPruningConfig::ICalcer> TBaseIndexComponent::CreatePruningCalcer(const NRTYServer::IIndexManagersStorage* managers) const {
    if (Config.Pruning->GetType() == TPruningConfig::GROUP_ATTR)
        return MakeHolder<TGrAttrPruningCalcer>(managers ? managers->GetIndexManager() : nullptr, Config.Pruning->ToString());
    return nullptr;
}

bool TBaseIndexComponent::TuneSearch(TBaseCommonSearch* search, IRemapperUrlDocId& remapper, TRTYIndexData* data) const {
    if (!ExternalSearchCreator) {
        return false;
    }

    const NRTYServer::IExternalSearchCreator::TParams params = {
        /*Search=*/*search,
        /*UrlDocId=*/remapper,
        /*IndexData=*/data
    };
    TAutoPtr<IExternalSearch> externalSearch = ExternalSearchCreator->Create(params);
    CHECK_WITH_LOG(externalSearch);
    CHECK_WITH_LOG(search);
    search->SetExternalSearch(externalSearch.Release(), Config.GetSearcherConfig().ExternalSearch.data());
    return true;
}

NRTYServer::IIndexComponent::TPriorityInfo TBaseIndexComponent::GetPriority() const {
    if (Config.IndexGenerator == GetName()) {
        return TPriorityInfo(DEFAULT_PRIMARY_GENERATOR_PRIORITY);
    } else {
        return TPriorityInfo(DEFAULT_SECONDARY_GENERATOR_PRIORITY);
    }
}

bool TBaseIndexComponent::HasIndexFiles(const TString& path) const {
    return NRTYServer::HasTextWad(path) || NRTYServer::HasYndex(path);
}

bool TBaseIndexComponent::IsEmptyIndex(const TString& path) const {
    return !(NRTYServer::HasTextWad(path) || NRTYServer::HasYndex(path));
}

bool TBaseIndexComponent::DoMergeMeta(const NRTYServer::TMergeContext& context) const {
    for (ui32 i = 0; i < context.Context.Dests.size(); ++i) {
        TIndexMetadataProcessor proc(context.Context.Dests[i]);
        proc->SetIsPrefixed(Config.IsPrefixedIndex);
    }
    return true;
}

bool TBaseIndexComponent::DoCheckMeta(const NRTYServer::TIndexMetadata& metaData) const {
    if (metaData.HasIsPrefixed() && metaData.GetIsPrefixed() != Config.IsPrefixedIndex) {
        FATAL_LOG << "Incorrect index usage for current config: key Prefixed different" << Endl;
        return false;
    }
    return true;
}

void TBaseIndexComponent::ClearIndexFiles() {
    IndexFiles.clear();
}

void TBaseIndexComponent::RegisterIndexFile(const TIndexFile& file) {
    if (!IndexFiles.insert(file).second) {
        WARNING_LOG << "Index file " << file.Name << " is registered more than once";
    }
}

NRTYServer::IIndexComponent::TIndexFile TBaseIndexComponent::GetIndexFile(const TIndexFileEntry& entry, TIndexFile::EPrefetchPolicy policy, bool checked) {
    TIndexFile result(entry.Name, checked, policy);
    result.PrefetchLower = entry.Lower;
    result.PrefetchUpper = entry.Upper;
    return result;
}

NRTYServer::EUrlIdSource TBaseIndexComponent::GetUrlIdSource() const {
    return ExternalSearchCreator ? ExternalSearchCreator->GetUrlIdSource() : NRTYServer::EUrlIdSource::External;
}

void TBaseIndexComponent::CorrectSearchConfig(TSearchConfig& config, NRTYServer::TSearcherConfig::TSearchConfigType type) {
    Y_UNUSED(type);
    if (ExternalSearchCreator) {
        ExternalSearchCreator->CorrectSearchConfig(config);
    }
}
