#include "erf_parsed_entity.h"
#include "erf_component.h"
#include "erf_global.h"

#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/factors/factors_config.h>
#include <saas/rtyserver/factors/factors_domain.h>
#include <saas/rtyserver/factors/rank_model.h>
#include <saas/rtyserver/indexer_core/document_parser.h>
#include <saas/library/behaviour/behaviour.h>

TRTYErfComponentParser::TRTYErfComponentParser(const TRTYErfIndexComponent& component)
{
    auto pruningFormula = component.GetPruningFormula();
    auto factors = component.GetConfig().GetSearcherConfig().Factors;
    if (pruningFormula && !!factors) {
        for (auto&& f : pruningFormula->GetUsedFactors()) {
            try {
                PruningFactors.insert(factors->GetFactorByFormulaIndex(f)->Name);
            } catch (...) {
                FAIL_LOG("Incorrect factors configuration: %d", f);
            }
        }
    }
}

void TRTYErfComponentParser::Parse(TParsingContext& context) const {
    bool needInCheckFullFactorsList =
        context.Command != NRTYServer::TMessage::DEPRECATED__UPDATE_DOCUMENT &&
        context.Command != NRTYServer::TMessage::DELETE_DOCUMENT &&
        GetBehaviour(context.Command).IsContentMessage;

    const NRTYFactors::TConfig& facConfig = *context.Result.GetConfig().GetSearcherConfig().Factors;
    TSet<TString> factorsUsage;
    TRTYErfParsedEntity* result = context.Result.GetComponentEntity<TRTYErfParsedEntity>(ERF_COMPONENT_NAME);
    VERIFY_WITH_LOG(result, "there is not Erf entity in parsed doc");

    result->SetFactorsInfo(&facConfig.StaticFactors());
    result->SetIgnoredFactorsInfo(&facConfig.IgnoredFactors());
    result->AddFactors(context.Document.GetFactors().GetNames(), context.Document.GetFactors().GetValues(), needInCheckFullFactorsList);

    if (context.Command == NRTYServer::TMessage::DEPRECATED__UPDATE_DOCUMENT &&
        context.Result.GetConfig().Pruning->GetType() == TPruningConfig::FORMULA)
    {
        auto documentFactors = context.Document.GetFactors();
        for (ui32 i = 0; i < documentFactors.NamesSize(); ++i) {
            if (PruningFactors.contains(documentFactors.GetNames(i))) {
                RestoreIsNeeded(context.Result);
                break;
            }
        }
    }
}

TRTYErfParsedEntity::TRTYErfParsedEntity(TConstructParams& params)
    : TParsedDocument::TParsedEntity(params)
{}

bool TRTYErfParsedStorage::Apply(TBasicFactorStorage& data) const {
    bool result = false;
    for (ui32 i = 0; i < Values.Size(); ++i) {
        if (Checks[i]) {
            result = true;
            if (Actions[i] == NRTYServer::TMessage::TFactorValues::ADD)
                data[i] += Values[i];
            else
                data[i] = Values[i];
        }
    }
    return result;
}

void TRTYErfParsedStorage::DoMerge(const TRTYErfParsedStorage& entity, bool forceValueSubstitution) {
    CHECK_WITH_LOG(FactorsInfo->GetStaticFactorsCount() == entity.FactorsInfo->GetStaticFactorsCount());
    for (ui32 i = 0; i < entity.Values.Size(); ++i) {
        if (entity.Checks[i] && (forceValueSubstitution || !Checks[i])) {
            Values[i] = entity.Values[i];
            Checks[i] = true;
        }
    }
}

void TRTYErfParsedEntity::MergeToProto(NRTYServer::TParsedDoc& pd, const NRTYServer::TDocSerializeContext& context) const {
    TParsedEntity::MergeToProto(pd, context);
    CHECK_WITH_LOG(FactorsInfo);
    for (ui32 i = 0; i < FactorsInfo->GetStaticFactorsCount(); ++i) {
        if (!GetChecks()[i]) {
            continue;
        }

        NRTYServer::TMessage::TErfInfo* erfInfo = pd.MutableDocument()->MutableFactors();
        erfInfo->AddNames(FactorsInfo->GetFactor(i).Name);
        NRTYServer::TMessage::TFactorValues::TValue* factorValue = erfInfo->MutableValues()->AddValues();
        factorValue->SetValue(Values[i]);
        factorValue->SetAction(Actions[i]);
    }
}

void TRTYErfParsedEntity::DoApplyPatch(const TParsedDocument& doc)
{
    const TRTYErfParsedEntity* patchEntity = doc.GetComponentEntity<TRTYErfParsedEntity>(ERF_COMPONENT_NAME);
    if (!patchEntity)
        return;
    patchEntity->Apply(Values);
    for (ui32 i = 0; i < patchEntity->GetChecks().size(); ++i) {
        if (patchEntity->GetChecks()[i]) {
            SetValue(i, patchEntity->GetValues()[i], patchEntity->Actions[i], true);
        }
    }
}

bool TRTYErfParsedEntity::FillFactorStorage(TFactorView& factorStorage) const {
    for (ui32 i = 0; i < FactorsInfo->GetStaticFactorsCount(); ++i) {
        factorStorage[FactorsInfo->GetFactor(i).IndexGlobal] = Values[i];
    }
    return true;
}

bool TRTYErfParsedStorage::GetFactorValue(const TString& name, ui32 index, bool strict, TFactorValue& value) const {
    THashMap<TString, TFactorValue>::const_iterator iter = ValueByFactorName.find(name);
    if (iter != ValueByFactorName.end()) {
        value = iter->second;
        return true;
    } else if (strict) {
        const NRTYFactors::TSimpleFactorDescription& factor = FactorsInfo->GetFactor(index);
        if (factor.DefaultValue.IsSet()) {
            value.SetValue(factor.DefaultValue);
            value.SetAction(NRTYServer::TMessage::TFactorValues::SET);
            return true;
        } else
            ythrow yexception() << "factor " << name << " value is not set and cannot be deduced";
    } else
        return false;
}

void TRTYErfParsedStorage::AddFactors(const TRTYFactorsNamesProto& namesInfo, const NRTYServer::TMessage::TFactorValues& valuesInfo, bool strictFactorCount) {
    CHECK_WITH_LOG(FactorsInfo);
    ValueByFactorName.clear();

    if (namesInfo.size() != valuesInfo.GetValues().size())
        throw yexception() << "incorrect ERF protobuf: number of factor names and values differ";

    for (int i = 0; i < namesInfo.size(); ++i) {
        const TString& name = namesInfo.Get(i);
        if (ValueByFactorName.contains(name))
            throw yexception() << "factor " << name << " is present twice";
        ui32 index;
        if (!FactorsInfo->CheckFactorName(name, index)) {
            if (IgnoredFactorsInfo && IgnoredFactorsInfo->CheckFactorName(name, index)) {
                continue;
            }
            throw yexception() << "unknown factor " << name;
        }
        ValueByFactorName[name] = valuesInfo.GetValues(i);
    }

    for (ui32 i = 0; i < FactorsInfo->GetStaticFactorsCount(); ++i) {
        const TString& name = FactorsInfo->GetFactor(i).Name;
        TFactorValue value;
        if (GetFactorValue(name, i, strictFactorCount, value)) {
            SetValue(i, value.GetValue());
            Actions[i] = value.GetAction();
        }
    }
}

void TRTYErfParsedStorage::SetIgnoredFactorsInfo(const IRTYStaticFactors* factorsInfo) {
    IgnoredFactorsInfo = factorsInfo;
}

TRTYStaticFactorsConfig::TRTYStaticFactorsConfig(const NRTYFactors::TConfig* config)
{
    if (config)
        FactorsData.Reset(new TErfStaticFactors(config->StaticFactors()));
}
