#include "searchserverconfig.h"

#include <saas/library/metasearch/cgi/meta_cgi_filter.h>
#include <saas/library/metasearch/helpers/rearrange.h>

#include <library/cpp/logger/global/global.h>

#include <util/datetime/base.h>
#include <util/folder/dirut.h>

namespace {
    const TString ClickLoggingRule = "RTYRedirect";
}

TSearchServerConfig::TSearchServerConfig() {
    AsyncSearch = true;
    IsNeedInStatInfo = true;
    WorkDir = CurrentFolder;
    ProtoCollection_.SetDestroyQueueSize(15);
    ProtoCollection_.SetDestroyThreads(1);
    TwoStepQuery = false;
    InsensitiveClientNumFetch = false;
    SwitchToNextSourceFetchStage = false;
    EnableWildCards = "infix";
    ProtoCollection_.SetEnableCacheForDbgRlv(true);
    CommonSourceOptions.TwoStepQuery = TwoStepQuery;
    TiersLimitTable.push_back(0);
    DefaultScatterOptions.ConnectTimeouts.push_back(
        TDuration::MilliSeconds(30));
    DefaultScatterOptions.SendingTimeouts.push_back(
        TDuration::MilliSeconds(30));
    DefaultScatterOptions.TimeoutTable.push_back(
        TDuration::MilliSeconds(100));
    IncompleteTasksCheckInterval =
        DefaultScatterOptions.TimeoutTable.front() / 2;
    CommonSourceOptions.TimeOut = DefaultScatterOptions.TimeoutTable.front();
    CommonSourceOptions.AllowDynamicWeights = false;
    CommonSourceOptions.EnableIpV6 = true;
    MergeOptions.SkipSameDocids = false;
    CgiFilterOptions.Merge(TRTYMetaCgiParams::GetParamsToBackend(), {});
    UserDirectives["numbers_style"] = "NO_ZEROS";
    AddRequestPath = false;
}

void TSearchServerConfig::ParseDirectives(const TYandexConfig::Directives& directives) {
    Y_CONFIG_READ_UI32(directives, "Threads", ProtoCollection_.SetRequestThreads);
    Y_CONFIG_READ_UI32(directives, "MaxQueueSize", ProtoCollection_.SetRequestQueueSize);
    Y_CONFIG_READ_UI32(directives, "PassagesThreads", ProtoCollection_.SetSnippetThreads);
    Y_CONFIG_READ_UI32(directives, "PassagesQueueSize", ProtoCollection_.SetSnippetQueueSize);
    Y_CONFIG_READ_UI32(directives, "MaxDestroyQueueSize", ProtoCollection_.SetDestroyQueueSize);
    Y_CONFIG_READ_STRING(directives, "RouteHashType", ProtoCollection_.SetRouteHashType);

    TString languages;
    if (directives.GetValue("MorphologyLanguages", languages)) {
        if (languages.empty())
            MorphologyLanguages = LI_DEFAULT_REQUEST_LANGUAGES;
        else if (TCiString("non") == languages.data())
            MorphologyLanguages = TLangMask();
        else
            MorphologyLanguages = NLanguageMasks::CreateFromList(languages);
    }
    TString preferedLangs("ru,en");
    directives.GetValue("PreferedMorphologyLanguages", preferedLangs);
    PreferedMorphologyLanguages =
        NLanguageMasks::CreateFromList(preferedLangs);

    if (directives.contains("ConnectTimeout")) {
        DefaultScatterOptions.SendingTimeouts = DefaultScatterOptions.ConnectTimeouts;
    }
    DefaultScatterOptions.TimeoutTable.front() = TDuration::MilliSeconds(
        directives.Value("GlobalTimeout",
            DefaultScatterOptions.TimeoutTable.front().MilliSeconds()));
    IncompleteTasksCheckInterval = TDuration::MilliSeconds(
        directives.Value("SwitchToSlaveTimeout",
            DefaultScatterOptions.TimeoutTable.front().MilliSeconds() / 2));

    CommonSourceOptions.TimeOut = DefaultScatterOptions.TimeoutTable.front();
    if (Sections.contains("QueryCache")) {
        CommonSourceOptions.CacheLifeTime = CacheOptions.Main().CacheLifeTime;
    }

    TString tmp;
    if (directives.GetValue("LoadLog", tmp)) {
        resolvepath(tmp, WorkDir);
        LoadLog = tmp;
    }

    if (directives.GetValue("EventLog", tmp)) {
        resolvepath(tmp, WorkDir);
        ProtoCollection_.SetEventLog(tmp);
    }

    if (directives.GetValue("TwoStepQuery", TwoStepQuery)) {
        CommonSourceOptions.TwoStepQuery = TwoStepQuery;
    }

    directives.GetValue("ClicksLogging", ClicksLogging);
    directives.GetValue("SafeReport", SafeReport);
    directives.GetValue("AllowDynamicWeights", CommonSourceOptions.AllowDynamicWeights);
    TString tcp(TemplateCorrectionPolicy.data());
    directives.GetValue("TemplateCorrectionPolicy", tcp);
    TemplateCorrectionPolicy = tcp.data();

    if (ClicksLogging) {
        NUtil::TRearrangeOptionsHelper helper(*ProtoCollection_.MutableReArrangeOptions());
        if (!helper.HasRule(ClickLoggingRule)) {
            ERROR_LOG << "Cannot enable clicks logging: RTYRedirect is missing" << Endl;
        } else {
            NSc::TValue& config = helper.RuleScheme(ClickLoggingRule);
            if (config["url"].StringEmpty()) {
                WARNING_LOG << "Redirect URL is empty" << Endl;
            }
            config["ReplaceUrl"] = true;
        }
    }

    Y_CONFIG_PARSE_NON_EMPTY(directives, MetaRankingOptions, ProtoCollection_, MetaRankingOptions);
}

void TSearchServerConfig::ParseWizardConfig() {
    TYandexConfig::TSectionsMap::const_iterator iter =
        Sections.find("TWizardMain");
    if (iter != Sections.end()) {
        TYandexConfig dummy;
        TYandexConfig::Section wizardSection(*iter->second);
        wizardSection.Child = &wizardSection;
        wizardSection.Next = nullptr;
        WizardConfig.Reset(
            TWizardConfig::CreateWizardConfig(dummy, &wizardSection).Release());
    }
}

void TSearchServerConfig::ParseMisspell() {
    TYandexConfig::TSectionsMap::const_iterator iter =
        Sections.find("Misspell");
    if (iter != Sections.end()) {
        MisspellCorrectorConfig.Reset(
            new TMisspellCorrectorConfig(iter->second->GetDirectives()));
    }
}

void TSearchServerConfig::AdjustForMetaService() {
    CgiFilterOptions.Merge(TRTYMetaCgiParams::GetParamsToService(), {});
    TwoStepQuery = false;
    CommonSourceOptions.TwoStepQuery = false;
    MetaService = true;
}

void TSearchServerConfig::InitFromSection(TYandexConfig::Section* section, TConfigPatcher& /*preprocessor*/) {
    Sections = section->GetAllChildren();

    TYandexConfig dummy;
    CHECK_WITH_LOG(TSearchConfig::Init(dummy, section));

    ParseDirectives(section->GetDirectives());
    ParseMisspell();
    ParseWizardConfig();
}

void TSearchServerConfig::SetWorkDir(const TString& workDir) {
    WorkDir = workDir;
}
