#include "search_params_script.h"
#include <saas/deploy_manager/scripts/interface/interface.pb.h>

#include <saas/deploy_manager/server/client/client.h>

#include <library/cpp/json/writer/json_value.h>

#include <util/string/vector.h>
#include <util/generic/singleton.h>
#include <util/generic/map.h>
#include <util/charset/utf8.h>
#include <library/cpp/charset/wide.h>

namespace {

    using namespace NDMInterface;

    enum TFlag {
        REPEATED = 1
    };
    typedef ui8 TFlags;

    struct TParam {
        struct TValue {
            TValue(const TVector<TString>& nameCgi)
                : DisplayedName(nameCgi[0])
                , Cgi(nameCgi[1])
            {}

            TString DisplayedName;
            TString Cgi;
        };

        TParam(TConfigField::TType type, const char* name, const char* value, const char* description, TFlags flags)
            : Type(type)
            , Name(name)
            , Description(description)
            , Flags(flags)
        {
            if (type == TConfigField::ENUM) {
                TVector<TString> values = SplitString(value, ",");
                for (const auto& v : values)
                    Values.push_back(SplitString(v, "@", 2));
            } else
                Cgi = value;
        }

        inline bool HasFlag(TFlag flag) const {
            return Flags & flag;
        }

        TConfigField::TType Type;
        TString Name;
        TString Cgi;
        TString Description;
        TVector<TValue> Values;
    private:
        TFlags Flags;
    };

    class TGroup : public TVector<TParam> {
    public:
        enum TType { SEARCH, SUGGEST };
        TGroup()
            : Type(SEARCH)
        {}
        TType Type;
    };

    class TParams : public TVector<std::pair <TString, TGroup > > {
        inline void Reginster(const char* group, const char* name, TConfigField::TType type, const char* value, const char* description, TFlags flags = 0) {
            operator [] (group).push_back(TParam(type, name, value, description, flags));
        }

        inline value_type::second_type& operator[] (const value_type::first_type& key) {
            for (auto& t : *this)
                if (t.first == key)
                    return t.second;
            push_back(std::make_pair(key, TGroup()));
            return back().second;
        }
    public:
        TParams() {
            Reginster("Основное", "kps", TConfigField::INTEGER, "kps", "Префикс", REPEATED);
            Reginster("Основное", "numdoc", TConfigField::INTEGER, "numdoc", "Количество документов на странице");
            Reginster("Основное", "p", TConfigField::INTEGER, "p", "Номер страницы");
            Reginster("Основное", "g", TConfigField::STRING, "g", "Группировка");
            Reginster("Основное", "asc", TConfigField::BOOLEAN, "asc=yes", "Обратная сортировка");
            Reginster("Ранжирование", "formula", TConfigField::STRING, "relev=formula", "Основная формула ранжирования");
            Reginster("Ранжирование", "fast_formula", TConfigField::STRING, "relev=fast_formula", "Формула ранжирования для fast rank");
            Reginster("Ранжирование", "user_factor", TConfigField::STRING, "relev", "Вырожение для вычисления пользовательского фактора", REPEATED);
            Reginster("Ранжирование", "relev", TConfigField::STRING, "relev", "Настройки ранжирования", REPEATED);
            Reginster("Метапоиск", "msp", TConfigField::ENUM, "no@msp=no,force@msp=force,try_at_first@msp=try_at_first", "Политика опечаточника");
            Reginster("Саджест", "sgkps", TConfigField::INTEGER, "kps", "Префикс", REPEATED);
            Reginster("Саджест", "Max sentence", TConfigField::BOOLEAN, "rearr=sentence", "Включить правило Max sentence");
            Reginster("Саджест", "Next word", TConfigField::BOOLEAN, "rearr=nextword", "Включить правило Next word");
            Reginster("Саджест", "Cut tail", TConfigField::BOOLEAN, "rearr=cuttail", "Включить правило Cut tail");
            operator [] ("Саджест").Type = TGroup::SUGGEST;
        }
    };

    inline void InsertString(NJson::TJsonValue& json, const char* key, TString value) {
        if (!IsUtf(value))
            value = WideToUTF8(CharToWide(value, csYandex));
        json.InsertValue(key, value);
    }
}

bool NRTYDeploy::TScriptSearchParams::Process(IDeployInfoRequest& request) {
    NJson::TJsonValue report;
    for (const auto& group : *Singleton<TParams>()) {
        NJson::TJsonValue& groupJson = report.AppendValue(NJson::JSON_MAP);
        InsertString(groupJson, "group", group.first);
        switch (group.second.Type) {
            case TGroup::SEARCH:
                groupJson.InsertValue("group_type", "SEARCH");
                break;
            case TGroup::SUGGEST:
                groupJson.InsertValue("group_type", "SUGGEST");
                break;
        }
        NJson::TJsonValue& paramsJson = groupJson.InsertValue("params", NJson::JSON_ARRAY);
        for (const auto& param : group.second) {
            NJson::TJsonValue& paramJson = paramsJson.AppendValue(NJson::JSON_MAP);
            paramJson.InsertValue("label", param.Name);
            paramJson.InsertValue("type", TConfigField::descriptor()->FindEnumTypeByName("TType")->FindValueByNumber(param.Type)->name());
            paramJson.InsertValue("repeated", param.HasFlag(REPEATED));
            InsertString(paramJson, "description", param.Description);
            if (!!param.Cgi)
                paramJson.InsertValue("cgi", param.Cgi);

            if (!param.Values.empty()) {
                NJson::TJsonValue& valuesJson = paramJson.InsertValue("values", NJson::JSON_ARRAY);
                for (const auto& val : param.Values) {
                    NJson::TJsonValue& valueJson = valuesJson.AppendValue(NJson::JSON_MAP);
                    valueJson.InsertValue("value", val.DisplayedName);
                    valueJson.InsertValue("cgi", val.Cgi);
                }
            }
        }
    }
    request.Output() << "HTTP/1.1 200 \r\n";
    request.Output() << "Content-Type:application/json; charset=utf-8\r\n";
    request.Output() << "\r\n";
    request.Output() << report.GetStringRobust();
    return true;
}

NRTYDeploy::TScriptSearchParams::TFactory::TRegistrator<NRTYDeploy::TScriptSearchParams> NRTYDeploy::TScriptSearchParams::Registrator("search_params");
