#include "rty_external_search.h"
#include "rty_index_data.h"
#include "rty_relevance.h"
#include "rty_features.h"

#include <saas/library/metasearch/cgi/meta_cgi_filter.h>
#include <saas/rtyserver/factors/function.h>
#include <saas/rtyserver/search/cgi/rty_external_cgi.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/components/fastarchive/manager.h>
#include <saas/rtyserver/components/zones_makeup/makeup_manager.h>
#include <saas/rtyserver/components/prop/manager.h>

#include <search/grouping/groupinfo.h>

#include <library/cpp/json/json_writer.h>

void TRTYMetaCustomCgi::TreatCgiParams(TRequestParams& rp, const TCgiParameters& cgiParams) {
    Y_UNUSED(cgiParams);
    if (GlobalConfig.GetSearcherConfig().ReArrangeOptions.find("StringSort") != TString::npos) {
        for (auto&& gp : rp.GroupingParams) {
            if (gp.gAttrToSort.StartsWith("gta_")) {
                rp.NGroupsForSourceLowerBound = MAX_GROUP_COUNT * 0.99;
            }
        }
    }
}

const IFactorsInfo* TRTYExternalSearch::GetFactorsInfo() const {
    return IndexData->GetFactorsConfig();
}

IIndexData* TRTYExternalSearch::GetIndexData() {
    return IndexData;
}

TRTYExternalSearch::TRTYExternalSearch(const NRTYServer::IExternalSearchCreator::TParams& params)
    : Owner(params.Search)
    , FactorsMaskCache(*params.IndexData->GetFactorsConfig())
    , Remapper(params.UrlDocId)
    , IndexData(params.IndexData)
{
}

void TRTYExternalSearch::Init(const TSearchConfig*) {
    const NRTYServer::TFactorMasks& allFactorsInConfig = FactorsMaskCache.GetAll();
    const auto& dpFactors = allFactorsInConfig.RTYMask.PluginFactors;
    const bool hasRtyDynamicFeatures = !dpFactors.empty();

    if (hasRtyDynamicFeatures) {
        const TString& factorModelsDir = IndexData->GetRTYSearcherConfig()->FactorsModels;
        NRTYFeatures::InitModels(dpFactors, IndexData->GetFactorsConfig(), factorModelsDir); // may involve significant I/O
    }
}

void TRTYExternalSearch::SerializeFirstStageAttributes(ui32 docId, TArrayRef<const char * const> attrNames, IAttributeWriter& write) const {
    THashSet<TStringBuf> attrSet{attrNames.begin(), attrNames.end()};
    const TRTYFastArchiveManager* fastArch = IndexData->GetFastArchiveManager();
    if (Y_UNLIKELY(fastArch)) {
        const IFastArchiveDoc* doc = fastArch->GetDoc(docId);
        if (doc) {
            for (IFastArchiveDoc::TIndex i = 0; i < doc->GetPropertiesCount(); ++i) {
                if (attrSet.contains(doc->GetPropertyName(i))) {
                    for (const auto& val : doc->GetPropertyValues(i)) {
                        write(doc->GetPropertyName(i), val);
                    }
                }
            }
        }
    }

    const auto* prop = IndexData->GetPropManager();
    if (prop && prop->GetConfig()->GetEnableFsgta()) {
        prop->WalkDocumentProperties(docId, [&](TStringBuf name, TStringBuf value) {
            if (attrSet.contains(name)) {
                write(name, value);
            }
        });
    }

    constexpr TStringBuf rtyFactors{"_RtyFactors"};

    if (attrSet.contains(rtyFactors)) {
        const TString configVersion = ::IntToString<10>(IndexData->GetFactorsConfig()->GetVersion());
        write(rtyFactors, configVersion);
    }

    const NRTYFeatures::TImportedFunctions* componentGtas = IndexData->GetImportedFunctions();
    if (Y_UNLIKELY(componentGtas)) {
        const auto& hashMap = componentGtas->GetFunctions().ComponentGtas;
        for (const auto& name: attrNames) {
            TStringBuf key = name;
            if (key.SkipPrefix("_")) {
                if (const auto* func = hashMap.FindPtr(key)) {
                    write(name, (*func)(docId));
                }
            }
        }
    }
}

void TRTYExternalSearch::FillCustomProperties(TSearcherProps* props, const TRequestParams* rp) const {
    if (!!rp->ExternalCgi) {
        TRTYCgiReader* cgi = dynamic_cast<TRTYCgiReader*>(rp->ExternalCgi.Get());
        Y_ASSERT(cgi);

        if (cgi) {
            cgi->FillProperties(props);
        }
    }
}

IRelevance* TRTYExternalSearch::CreateRelevance(const TBaseIndexData& baseIndexData, TIndexAccessors& /*accessors*/, const TRequestParams* rp) const {
    if (IndexData->GetFactorsConfig()->IsInitialized()) {
        auto cgi = CheckedCast<IRTYCgiReader*>(rp->ExternalCgi.Get());
        Y_ASSERT(cgi);
        return new TRTYRelevance(FactorsMaskCache, IndexData, *cgi, baseIndexData, rp);
    } else {
        return nullptr;
    }
}

const IUrlIdInfo* TRTYExternalSearch::GetUrlIdInfo() const {
    return IndexData->GetRTYSearcherConfig()->EnableUrlHash ? IndexData->GetDDKManager()->GetUrlIdInfo() : nullptr;
}

void TRTYExternalSearch::PrepareRequestParams(TRequestParams* rp) const {
    rp->ExternalCgi.Reset(new TRTYCgiReader(Owner.GetArchiveData(), Remapper, *IndexData));
    rp->IgnoreWebQuorum = true;
    if (IndexData->GetRTYSearcherConfig()->SnippetsDeniedZonesVector.size())
        rp->SnippetsParams.SentsFilter = IndexData->GetMakeupManager();
}

void TRTYExternalSearch::TuneRequestParams(TRequestParams* rp) const {
    rp->KeepAllDocuments = IndexData->GetRTYSearcherConfig()->KeepAllDocuments;
    NRTYServer::TSearcherConfig::TFiltrationModelType ft = IndexData->GetRTYSearcherConfig()->FiltrationModel;
    if (ft != NRTYServer::TSearcherConfig::fmWeb) {
        rp->FiltrationType = ft;
        rp->FiltrationModelFactory = &FiltrationModelFactory;
    }
}

bool TRTYExternalSearch::ProcessInfoRequest(const TRequestParams& rp, TBufferStream& outputStream) const {
    if (rp.InfoParams.Type == "indexstat") {
        if (const NRTYServer::IRTYIndexManager* manager = IndexData->GetIndexManager()) {
            NJson::TJsonValue json;
            if (manager->GetIndexInfo(json)) {
                WriteJson(&outputStream, &json);
                return true;
            }
        }
    }

    return false;
}

bool TRTYExternalSearch::UseMemoryPool() const {
    return true; // without memory pool we are not able to get RankingFactors
}

namespace {

    class TRTYExternalSearchCreator: public NRTYServer::IExternalSearchCreator {
        TAutoPtr<IExternalSearch> Create(const NRTYServer::IExternalSearchCreator::TParams& params) const override {
            return MakeHolder<TRTYExternalSearch>(params).Release();
        }

        TSimpleSharedPtr<ICustomCgi> CreateMetaCgi(const TRequestParams& rp, const TRTYServerConfig& globalConfig) const override {
            Y_UNUSED(rp);
            return MakeSimpleShared<TRTYMetaCustomCgi>(globalConfig);
        }

        TCgiFilterOptions CreateMetaCgiFilter() const override {
            const auto& params = TRTYMetaCgiParams::GetParamsToBase();
            return TCgiFilterOptions({params.begin(), params.end()}, {});
        }

        NRTYServer::EUrlIdSource GetUrlIdSource() const override {
            return NRTYServer::EUrlIdSource::DDK;
        }
    };
}

NRTYServer::IExternalSearchCreator::TFactory::TRegistrator<TRTYExternalSearchCreator> RTYExternalSearch("rty_relevance");
