#pragma once

#include <util/generic/string.h>
#include <util/string/cast.h>

namespace NSaas {

namespace NJsonFormat {
    // root zone fields
    const TString PrefixLable = "prefix";
    const TString ActionLable = "action";
    const TString DocsLable = "docs";
    const TString RequestLable = "request";
    const TString ExternalShardLable = "shard";

    // document fields
    const TString UrlLable = "url";
    const TString BodyLable = "body";
    const TString IndexedDocLable = "inddoc";

    // zone fields
    const TString ChildrenLable = "children";
    const TString OptionsLable = "options";
    const TString TypeLable = "type";
    const TString ValueLable = "value";

    // document options
    const TString CheckOnlyBeforeReplyLable = "check_only_before_reply";
    const TString RealtimeLable = "realtime";
    const TString VersionLable = "version";
    const TString FilterRankLable = "filter_rank";
    const TString DatabaseVersionLable = "db_version";
    const TString DeadlineLable = "deadline_min_utc";
    const TString MimeTypeLable = "mime_type";
    const TString CharsetLable = "charset";
    const TString LanguageLable = "language";
    const TString Language2Lable = "language2";
    const TString LanguageDefaultLable = "language_default";
    const TString LanguageDefault2Lable = "language_default2"; // deprecated
    const TString GroupLable = "group"; // deprecated
    const TString ModificationTimestampLable = "modification_timestamp";
    const TString UpdateTypeLable = "update_type";

    // special structures
    const TString ExtDataNameLable = "name";
    const TString ExtDataCompNameLable = "component_name";
    const TString ExtDataDataLable = "data";
    const TString QSFactorNamesLable = "factor_names";
    const TString QSFactorsLable = "factors";
    // annotations
    const TString AnnLable = "annotations";
    const TString AnnSentenceLable = "text";
    const TString AnnSentenceLangLable = "lang";
    const TString AnnSentenceRegionLable = "region";
    const TString AnnSentenceRegionsArrayLable = "regions";
    const TString AnnStreamsLable = "streams";
    // geo
    const TString GeoLayerKindLable = "kind";
    const TString CoordsLable = "coords";

    // misc
    const TString NoNameZone = "__no_name__";

    struct TEntity {
    public:
        enum EType {
            atZONE,
            atSEARCH_LITERAL,
            atSEARCH_INTEGER,
            atGROUP,
            atGROUP_LITERAL,
            atBODY,
            atURL,
            atPROPERTY,
            atFACTOR,
            atQS_INFO,
            atINT_QS_INFO,
            atCS_INFO,
            atINDDOC,
            atFSPROPERTY,
            atANNOTATION,
            atMINGEO,
            atSPECKEY,
            atINT_FACTOR,
            atTRIGRAM_INDEXING,
            atEMBEDDING,
        };
    public:
        EType Type;
        char ShortName;
        TString LongName;
    };

    const TEntity Entities[] = {
        { TEntity::atZONE,                  'z', "zone" },
        { TEntity::atSEARCH_LITERAL,        'l', "search_attr_literal" },
        { TEntity::atSEARCH_INTEGER,        'i', "search_attr_int" },
        { TEntity::atGROUP,                 'g', "group_attr" },
        { TEntity::atGROUP_LITERAL,         'h', "group_attr_literal" },
        { TEntity::atBODY,                  'b', "body" },
        { TEntity::atURL,                   'u', "url" },
        { TEntity::atPROPERTY,              'p', "prop"},
        { TEntity::atFSPROPERTY,            'm', "fsprop"},
        { TEntity::atFACTOR,                'f', "factor"},
        { TEntity::atQS_INFO,               'q', "qs_info" },
        { TEntity::atINT_QS_INFO,           'Q', "int_qs_info" },
        { TEntity::atCS_INFO,               'c', "cs_info" },
        { TEntity::atINDDOC,                'd', "inddoc" },
        { TEntity::atANNOTATION,            'a', "annotations" },
        { TEntity::atMINGEO,                'e', "geolayer" },
        { TEntity::atSPECKEY,               'k', "spec_key"},
        { TEntity::atINT_FACTOR,            'F', "int_factor"},
        { TEntity::atTRIGRAM_INDEXING,      't', "trigram_indexing_prop"},
        { TEntity::atEMBEDDING,             'E', "embeddings"},
    };

    class THashedEntities {
    public:
        static const TEntity& GetByShortName(char name) {
            return GetFromHashMap(name, Singleton<THashedEntities>()->ByShortName);
        }
        static const TEntity& GetByLongName(const TString& name) {
            return GetFromHashMap(name, Singleton<THashedEntities>()->ByLongName);
        }
        static const TEntity& GetByType(TEntity::EType type) {
            return GetFromHashMap(type, Singleton<THashedEntities>()->ByType);
        }
    public:
        THashedEntities() {
            for (ui32 i = 0; i < Y_ARRAY_SIZE(Entities); ++i) {
                ByType[Entities[i].Type] = Entities[i];
                ByShortName[Entities[i].ShortName] = Entities[i];
                ByLongName[Entities[i].LongName] = Entities[i];
            }
        }
    private:
        template <class TKey, class TValue>
        static inline const TValue& GetFromHashMap(const TKey& key, const THashMap<TKey, TValue>& container) {
            typename THashMap<TKey, TValue>::const_iterator i = container.find(key);
            if (i == container.end())
                throw yexception() << "unknown entity type " << KeyToString(key);
            return i->second;
        }

        template <class TKey>
        static inline TString KeyToString(const TKey& key) {
            return TString(key);
        }

        static inline TString KeyToString(TEntity::EType key) {
            return ToString(static_cast<int>(key));
        }

    private:
        THashMap<TEntity::EType, TEntity> ByType;
        THashMap<char, TEntity> ByShortName;
        THashMap<TString, TEntity> ByLongName;
    };
}

namespace NProtobufFormat {
    const TString QueryDelBodyPrefix = "query_del:";
    const TString QueryDelUrlPrefix = "queryDel_";
}
}
