#include "const.h"
#include "config.h"
#include "manager.h"

#include <saas/rtyserver/search/processors/abstract/abstract.h>

#include <library/cpp/containers/stack_vector/stack_vec.h>

#include <util/string/split.h>

const TString TKeysIndexManager::KeyNameCgi = "_url";

ui32 TKeysIndexManager::GetDocumentsCount() const {
    return Max<ui32>();
}

ui32 TKeysIndexManager::RemoveDocids(const TVector<ui32>& /*docids*/) {
    return 0;
}

TKeysIndexManager::TKeysIndexManager(const TRTYServerConfig& config)
    : NRTYServer::IIndexComponentManager(NRTYServer::KeysComponentName)
{
    Config = config.ComponentsConfig.Get<TKeysComponentConfig>(ComponentName);
    CHECK_WITH_LOG(Config);
}

TKeysIndexManager::~TKeysIndexManager() {
}

void TKeysIndexManager::InitInteractions(const NRTYServer::IIndexManagersStorage& /*storage*/) {
}

void TKeysIndexManager::ExtractRequestParams(const TCgiParameters& cgi, TVector<TDocSearchInfo>& searchInfos, TString& keyName) const {
    const TString& kps = cgi.Get("sgkps");
    keyName = cgi.Get("key_name");

    TStackVec<ui64, 8> prefixes;
    if (!kps.empty()) {
        StringSplitter(kps).Split(',').ParseInto(&prefixes);
    } else {
        prefixes.reserve(1);
        prefixes.push_back(0);
    }

    if (cgi.Has("saas_no_text_split")) {
        for (const auto& text: cgi.Range("text")) {
            for (ui64 prefix : prefixes) {
                if (!text.empty()) {
                    searchInfos.emplace_back(text, prefix);
                }
            }
        }
    } else {
        const TString& text = cgi.Get("text");
        for (const auto key : StringSplitter(text).Split(',').SkipEmpty()) {
            const TString keyStr = TString(key.Token());
            for (ui64 prefix : prefixes) {
                searchInfos.emplace_back(keyStr, prefix);
            }
        }
    }
}

bool TKeysIndexManager::SearchImpl(const TRTYSearchRequestContext& context, const IIndexController& controller, TVector<TDocIdCandidate>& docIds, TMessagesCollector& errors) const {
    // This function returns docIdCandidates, which we should verify further, using fullArc or another source of documents' hashes
    TVector<TDocSearchInfo> searchInfos;
    TString keyName;
    ExtractRequestParams(context.CgiParams(), searchInfos, keyName);

    TComponentSearcher::TPtr compSearch;
    TAtomicSharedPtr<IDocProcessor> filter;
    if (context.UseFilters()) {
        compSearch = controller.InitSpecialSearch(context, errors);
        if (compSearch)
            filter = compSearch->CreateFilter();
        if (!filter)
            return false;
    }

    if (!keyName || keyName == KeyNameCgi) {
        if (!filter) {
            controller.RemapUrls2DocIdCandidates(searchInfos, docIds);
        } else {
            TVector<TDocIdCandidate> docs;
            controller.RemapUrls2DocIdCandidates(searchInfos, docs);
            for (auto&& doc : docs) {
                if (doc.IsDocIdSet() && !filter->DocumentBanned(doc.GetDocId())) {
                    docIds.push_back(doc);
                }
            }
        }
    } else {
        TVector<TDocIdCandidate> currentResult;
        for (auto&& sInfo : searchInfos) {
            TSet<SUPERLONG> localResult;
            TDocIdCandidate localCandidate(sInfo);
            localCandidate.SetVerified(true);
            if (GetData(keyName, sInfo.GetHash(), filter.Get(), localResult)) {
                for (const auto& pos : localResult) {
                    localCandidate.SetDocId(TWordPosition::Doc(pos));
                    currentResult.push_back(localCandidate);
                }
            }
        }

        for (auto&& i : currentResult) {
            if (!controller.IsRemoved(i.GetDocId())) {
                docIds.push_back(i);
            }
        }
    }

    return true;
}

