from boltons.dictutils import OneToOne
from mongoengine.queryset.transform import MATCH_OPERATORS


class ParameterKeyEscaper:
    """
    Makes the fields name ready for use with MongoDB and Mongoengine
    . and $ are replaced with their codes
    __ and leading _ are escaped
    Since % is used as an escape character the % is also escaped
    """

    _mapping = OneToOne({".": "%2E", "$": "%24", "__": "%_%_"})

    @classmethod
    def escape(cls, value: str):
        """ Quote a parameter key """
        value = value.strip().replace("%", "%%")

        for c, r in cls._mapping.items():
            value = value.replace(c, r)

        if value.startswith("_"):
            value = "%_" + value[1:]

        return value

    @classmethod
    def _unescape(cls, value: str):
        for c, r in cls._mapping.inv.items():
            value = value.replace(c, r)
        return value

    @classmethod
    def unescape(cls, value: str):
        """ Unquote a quoted parameter key """
        value = "%".join(map(cls._unescape, value.split("%%")))

        if value.startswith("%_"):
            value = "_" + value[2:]

        return value


def mongoengine_safe(field_name):
    if field_name in MATCH_OPERATORS:
        return field_name + "__"
    return field_name