#include "query.h"

#include <util/string/builder.h>
#include <util/string/cast.h>
#include <library/cpp/cgiparam/cgiparam.h>
#include <util/system/yassert.h>

namespace NSaas {

    namespace {
        struct THttpQuerySerializer {
            TStringBuilder Query;

            void AddPathPrefix(const TStringBuf prefix) {
                Query << prefix;
            }

            void AddParam(const TStringBuf name, const TString& value) {
                Query << "&" << name << "=" << value;
            }

            void AddExtraParam(const TStringBuf extra) {
                Query << extra;
            }
        };

        struct TAppHostQuerySerializer {
            NJson::TJsonValue Query = NJson::EJsonValueType::JSON_MAP;

            void AddPathPrefix(const TStringBuf /*prefix*/) {}

            void AddParam(const TStringBuf name, const TString& value) {
                NJson::TJsonValue::TMapType& queryMap = Query.GetMapSafe();

                auto existing = queryMap.find(name);
                if (existing != queryMap.end()) {
                    if (!existing->second.IsArray()) {
                        TString old = existing->second.GetString();
                        existing->second.AppendValue(old);
                    }
                    existing->second.AppendValue(value);
                } else {
                    Query[name] = value;
                }

            }

            void AddExtraParam(const TStringBuf extra) {
                for (const auto& param: TCgiParameters(extra)) {
                    AddParam(param.first, param.second);
                }
            }
        };
    } // namespace

    template <class TSerializer>
    void TQuery::SerializeQuery(TSerializer& s) const {
        if (IsSuggest) {
            s.AddPathPrefix("/suggest/?");
        } else if (CustomPathPrefix) {
            s.AddPathPrefix(TString::Join("/", CustomPathPrefix, "?"));
        } else {
            s.AddPathPrefix("/?");
        }
        Y_ENSURE(ValidateQuery(), "NSaas::TQuery::SerializeQuery: empty query text and qtree");
        ui32 textsCount = 0;
        for (const TString& text : Texts) {
            if (!!text) {
                s.AddParam("text", text);
                ++textsCount;
            }
        }
        if (!!QTree) {
            s.AddParam("qtree", QTree);
        }
        for (const auto& value: Properties) {
            s.AddParam("gta", value);
        }
        for (const auto& value: Relev) {
            s.AddParam("relev", value);
        }
        if (!IsGroupingDisabled) {
            s.AddParam("g", Grouping.GetQuery());
        }
        s.AddParam("how", How.ToString());
        s.AddParam("numdoc", ToString(NumDoc));

        if (NeedInSnippet) {
            s.AddParam("snip", "diqm=1");
        }
        if (Page > 0) {
            s.AddParam("p", ToString(Page));
        }
        if (Priority) {
            s.AddParam("ps", "yes");
        }
        if (Ascending) {
            s.AddParam("asc", "yes");
        }
        if (HrAnswer) {
            s.AddParam("hr", "da");
        }
        if (JsonFactors) {
            s.AddParam("dbgrlv", "da");
            s.AddParam("fsgta", "_JsonFactors");
        }
        if (SkipSearcherProps) {
            s.AddParam("skip_searcher_props", "da");
        }
        if (!!UserId) {
            s.AddParam("user_id", UserId);
        }
        if (!!Kps) {
            s.AddParam(KeyValue ? "sgkps": "kps", Kps);
        }
        if (!!Auth) {
            s.AddParam("auth", Auth);
        }
        for (const auto& pron : PronFlags) {
            s.AddParam("pron", pron);
        }
        if (!!WizardRules) {
            s.AddParam("rwr", WizardRules);
        }
        if (CompressionMethod != NMetaProtocol::CM_COMPRESSION_NONE) {
            s.AddParam("pron", "pc" + ToString(CompressionMethod));
        }
        if (KeyValue) {
            s.AddParam("meta_search", "first_found");
            if (NormalKVReport) {
                s.AddParam("normal_kv_report", "yes");
            }
            if (!KeyName.empty()) {
                s.AddParam("key_name", KeyName);
            }
            if (!SplitText) {
                s.AddParam("saas_no_text_split", "true");
            }
            s.AddParam("sp_meta_search", "multi_proxy");
        }
        if (BalancerTimeout) {
            s.AddParam("balancertimeout", ToString(BalancerTimeout->MilliSeconds()));
        }
        s.AddExtraParam(ExtraParams);
    }

    TString TQuery::BuildQuery() const {
        THttpQuerySerializer s;
        SerializeQuery(s);
        return s.Query;
    }

    NJson::TJsonValue TQuery::BuildAppHostQuery(const TString& service) const {
        TAppHostQuerySerializer s;
        SerializeQuery(s);
        s.Query["service"] = service;
        return s.Query;
    }

    bool TQuery::ValidateQuery() const {
        ui32 noEmptyCount = 0;
        for (const auto& text : Texts) {
            if (!text.empty()) {
                ++noEmptyCount;
            }
        }
        if (KeyValue) {
            return noEmptyCount;
        }

        return noEmptyCount == 1 || !!QTree;
    }
}
