import datetime
from enum import Enum

LDAP_NULL_DATE = "000001010000Z"


class Syntax(str, Enum):
    # fmt: off
    BINARY =             "1.3.6.1.4.1.1466.115.121.1.5"
    BOOLEAN =            "1.3.6.1.4.1.1466.115.121.1.7"
    CERTIFICATE =        "1.3.6.1.4.1.1466.115.121.1.8"
    COUNTRY_STRING =     "1.3.6.1.4.1.1466.115.121.1.11"
    DISTINGUISHED_NAME = "1.3.6.1.4.1.1466.115.121.1.12"
    DIRECTORY_STRING =   "1.3.6.1.4.1.1466.115.121.1.15"
    FAX_IMAGE =          "1.3.6.1.4.1.1466.115.121.1.23"
    GENERALIZED_TIME =   "1.3.6.1.4.1.1466.115.121.1.24"
    IA5_STRING =         "1.3.6.1.4.1.1466.115.121.1.26"
    INTEGER =            "1.3.6.1.4.1.1466.115.121.1.27"
    JPEG =               "1.3.6.1.4.1.1466.115.121.1.28"
    NUMERIC_STRING =     "1.3.6.1.4.1.1466.115.121.1.36"
    OCTET_STRING =       "1.3.6.1.4.1.1466.115.121.1.40"
    POSTAL_ADDRESS =     "1.3.6.1.4.1.1466.115.121.1.41"
    PRINTABLE_STRING =   "1.3.6.1.4.1.1466.115.121.1.44"
    TELEPHONE_NUMBER =   "1.3.6.1.4.1.1466.115.121.1.50"
    UTC_TIME =           "1.3.6.1.4.1.1466.115.121.1.53"
    # fmt: on


def ldap_to_python(value, syntax):
    from .ldapobject import LDAPObject

    if syntax == Syntax.GENERALIZED_TIME:
        value = value.decode("utf-8")
        if value == LDAP_NULL_DATE:
            # python cannot represent datetimes with year 0
            return datetime.datetime.min
        if value.endswith("Z"):
            return datetime.datetime.strptime(value, "%Y%m%d%H%M%SZ").replace(
                tzinfo=datetime.timezone.utc
            )
        return datetime.datetime.strptime(value, "%Y%m%d%H%M%S%z")

    if syntax == Syntax.INTEGER:
        return int(value.decode("utf-8"))

    if syntax in (Syntax.JPEG, Syntax.FAX_IMAGE):
        return value

    if syntax == Syntax.BOOLEAN:
        return value.decode("utf-8").upper() == "TRUE"

    if syntax == Syntax.DISTINGUISHED_NAME:
        return LDAPObject.get(id=value.decode("utf-8"))

    return value.decode("utf-8")


def python_to_ldap(value, syntax, encode=True):
    encodable = True
    if syntax == Syntax.GENERALIZED_TIME and isinstance(value, datetime.datetime):
        if value == datetime.datetime.min:
            value = LDAP_NULL_DATE
        elif not value.tzinfo or value.tzinfo == datetime.timezone.utc:
            value = value.strftime("%Y%m%d%H%M%SZ")
        else:
            value = value.strftime("%Y%m%d%H%M%S%z")

    if syntax == Syntax.INTEGER and isinstance(value, int):
        value = str(value)

    if syntax in (Syntax.JPEG, Syntax.FAX_IMAGE):
        encodable = False

    if syntax == Syntax.BOOLEAN and isinstance(value, bool):
        value = "TRUE" if value else "FALSE"

    if syntax == Syntax.DISTINGUISHED_NAME:
        value = value.id

    if not value:
        return None

    return value.encode() if encode and encodable else value


def listify(value):
    return value if isinstance(value, list) else [value]
