import os
import logging
import random

import django
import django.conf

import mini_buildd.config

LOG = logging.getLogger(__name__)

MBD_INSTALLED_APPS = (
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.messages",
    "django.contrib.admin",
    "django.contrib.sessions",
    "django.contrib.admindocs",
    "django.contrib.staticfiles",
    "mini_buildd")


class SMTPCreds():
    """
    SMTP creds string parser. Format "USER:PASSWORD@smtp|ssmtp://HOST:PORT".

    >>> d = SMTPCreds(":@smtp://localhost:25")
    >>> (d.user, d.password, d.protocol, d.host, d.port)
    ('', '', 'smtp', 'localhost', 25)
    >>> d = SMTPCreds("kuh:sa:ck@smtp://colahost:44")
    >>> (d.user, d.password, d.protocol, d.host, d.port)
    ('kuh', 'sa:ck', 'smtp', 'colahost', 44)
    """

    def __init__(self, creds):
        self.creds = creds
        at = creds.partition("@")

        usrpass = at[0].partition(":")
        self.user = usrpass[0]
        self.password = usrpass[2]

        smtp = at[2].partition(":")
        self.protocol = smtp[0]

        hopo = smtp[2].partition(":")
        self.host = hopo[0][2:]
        self.port = int(hopo[2])


def gen_django_secret_key():
    # use same randomize-algorithm as in "django/core/management/commands/startproject.py"
    return "".join([random.choice("abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)") for _i in range(50)])


def get_django_secret_key(home):
    """
    Create django's SECRET_KEY *once* and/or returns it.

    :param home: mini-buildd's home directory.
    :type home: string
    :returns: string -- the (created) key.
    """
    secret_key_filename = os.path.join(home, ".django_secret_key")

    # the key to create or read from file
    secret_key = ""

    if not os.path.exists(secret_key_filename):
        # use same randomize-algorithm as in "django/core/management/commands/startproject.py"
        secret_key = gen_django_secret_key()
        with os.fdopen(os.open(secret_key_filename, os.O_CREAT | os.O_WRONLY, 0o600), "w") as secret_key_fd:
            secret_key_fd.write(secret_key)
    else:
        with mini_buildd.fopen(secret_key_filename, "r") as existing_file:
            secret_key = existing_file.read()

    return secret_key


def configure(smtp_string):
    """Configure django."""
    LOG.info("Setting up django...")

    smtp = SMTPCreds(smtp_string)
    debug = "webapp" in mini_buildd.config.DEBUG

    django.conf.settings.configure(
        DEBUG=debug,

        DEFAULT_AUTO_FIELD="django.db.models.AutoField",  # django 3.2: https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys

        ALLOWED_HOSTS=["*"],

        EMAIL_HOST=smtp.host,
        EMAIL_PORT=smtp.port,
        EMAIL_USE_TLS=smtp.protocol == "ssmtp",
        EMAIL_HOST_USER=smtp.user,
        EMAIL_HOST_PASSWORD=smtp.password,

        AUTH_PASSWORD_VALIDATORS=[
            {
                "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
            },
            {
                "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
                "OPTIONS": {
                    "min_length": 8,
                }
            },
            {
                "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
            },
            {
                "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
            },
        ],

        DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3",
                               "NAME": mini_buildd.config.ROUTES["home"].path.join("config.sqlite")}},

        TIME_ZONE="Etc/UTC",  # Default time zone: Canonical UTC. Better than django's default 'America/Chicago'.
        USE_TZ=True,          # Django: "stores datetime information in UTC in the database": that's definitely what we want.
        USE_L10N=True,

        SECRET_KEY=get_django_secret_key(mini_buildd.config.ROUTES["home"].path.join()),
        ROOT_URLCONF="mini_buildd.urls",
        STATIC_URL=mini_buildd.config.URIS["static"]["static"].uri,
        ACCOUNT_ACTIVATION_DAYS=3,
        LOGIN_URL=mini_buildd.config.URIS["accounts"]["login"].uri,

        MIDDLEWARE=[
            "django.middleware.common.CommonMiddleware",
            "django.contrib.sessions.middleware.SessionMiddleware",
            "django.middleware.csrf.CsrfViewMiddleware",
            "django.contrib.auth.middleware.AuthenticationMiddleware",
            "django.contrib.messages.middleware.MessageMiddleware",
            "mini_buildd.views.ExceptionMiddleware"
        ],

        INSTALLED_APPS=MBD_INSTALLED_APPS,
        TEMPLATES=[
            {
                "BACKEND": "django.template.backends.django.DjangoTemplates",
                "DIRS": [
                    f"{mini_buildd.config.PY_PACKAGE_PATH}/mini_buildd/templates"
                ],
                "APP_DIRS": True,
                "OPTIONS": {
                    "debug": debug,
                    "context_processors": [
                        #: Django defaults as per ``django-admin startproject dummy`` (django 2.2.20)
                        "django.template.context_processors.debug",
                        "django.template.context_processors.request",
                        "django.contrib.auth.context_processors.auth",
                        "django.contrib.messages.context_processors.messages",
                        #: Custom
                        "mini_buildd.views.context",
                    ],
                    "builtins": [
                        "mini_buildd.builtins",
                        "django.templatetags.static",
                    ],
                },
            },
        ])

    django.setup()


def pseudo_configure():
    """
    Pseudo-configure django. Use this where you need mini-buildd's model classes, but no actual instance.

    Example: Sphinx doc creation, API clients for unpickling model instances.
    """
    django.conf.settings.configure(
        SECRET_KEY=gen_django_secret_key(),
        DATABASES={
            "default": {
                "ENGINE": "django.db.backends.sqlite3",
                "NAME": ":memory:",
            }
        },
        MIDDLEWARE=[],
        INSTALLED_APPS=MBD_INSTALLED_APPS)

    django.setup()
