"""Django settings for the `diop_core` project.

Configuration is done using a file defined in the environment variable
`SETTINGS_ENV` (that variable has to be present when launching the Django
application). Reading the file and processing the settings is implemented
through the [django-environ][dj-environ] project.

Example
-------
To change a configuration value, the settings file needs to contain an
environment variable using the identical name as documented here, e.g. to
configure the Diop LDAP authentication bind password expected in the variable
diop_core.settings.AUTH_LDAP_BIND_PASSWORD, a line like this will be required in
the settings file:

`AUTH_LDAP_BIND_PASSWORD=SuP3rS3kr3T`

Notes
-----
The API docs generated by [pdoc][pdoc] will only list those items that are
configured through the settings file. Items that are not intended to be changed
(and thus are hard-coded in diop_core.settings) will be omitted.

References
----------
For generic information on this file, see:

* https://docs.djangoproject.com/en/4.1/topics/settings/
* https://docs.djangoproject.com/en/4.1/ref/settings/

[dj-environ]: https://django-environ.readthedocs.io/
[pdoc]: https://pdoc.dev
"""

# ruff: noqa: E501 (line length)
# ruff: noqa: F401 (imported but unused)

import os
from pathlib import Path

import ldap
from django_auth_ldap.config import LDAPSearch

import environ


env = environ.Env(
    # set casting (=default values in case they're not set in the environment)
    DEBUG=(bool, False)
)
"""@private (skip in pdoc)"""


# read the file defined by the environment variable `SETTINGS_ENV`:
env.read_env(env.str("SETTINGS_ENV"))


SECRET_KEY = env.str("SECRET_KEY")

SITE_ID = 1
"""@private (skip in pdoc)"""


DEBUG = env.bool("DEBUG")
"""[Django debug setting][djdoc-debug],
**DON'T RUN WITH DEBUG TURNED ON IN PRODUCTION**!

[djdoc-debug]: https://docs.djangoproject.com/en/4.1/ref/settings/#debug
"""


# allowed hosts may be overridden in the private settings!
# having a pseudo-hostname here is handy during development (can be pointed to
# wherever using a corresponding entry in /etc/hosts)
ALLOWED_HOSTS = env.list(
    "ALLOWED_HOSTS", default=["127.0.0.1", "localhost", "diop.test"]
)

# required e.g. if running behind an nginx reverse proxy:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
"""@private (skip in pdoc)"""


# Application definition

INSTALLED_APPS = [
    "diop",
    # also use whitenoise in development (when started via `runserver`):
    "whitenoise.runserver_nostatic",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.humanize",
    "django.contrib.sessions",
    "django.contrib.sites",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django_q",
    "django_extensions",
]
"""@private (skip in pdoc)"""

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "django.contrib.sites.middleware.CurrentSiteMiddleware",
]
"""@private (skip in pdoc)"""

ROOT_URLCONF = "diop_core.urls"
"""@private (skip in pdoc)"""

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]
"""@private (skip in pdoc)"""

WSGI_APPLICATION = "diop_core.wsgi.application"
"""@private (skip in pdoc)"""


# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

DATABASES = {"default": env.db("DATABASE_URL")}
"""The [backend database][djdoc-db]. For Diop, only MySQL is tested.

[djdoc-db]: https://docs.djangoproject.com/en/4.1/ref/settings/#databases
"""

# Password validation
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]
"""@private (skip in pdoc)"""

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "handlers": {"console": {"class": "logging.StreamHandler"}},
    "loggers": {"django_auth_ldap": {"level": "DEBUG", "handlers": ["console"]}},
}
"""@private (skip in pdoc)"""

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "django_auth_ldap.backend.LDAPBackend",
]
"""@private (skip in pdoc)"""

AUTH_LDAP_BIND_DN = env.str("AUTH_LDAP_BIND_DN")
AUTH_LDAP_BIND_PASSWORD = env.str("AUTH_LDAP_BIND_PASSWORD")
AUTH_LDAP_SERVER_URI = env.str("AUTH_LDAP_SERVER_URI")
AUTH_LDAP_BASE_DN = env.str("AUTH_LDAP_BASE_DN")


AUTH_LDAP_GLOBAL_OPTIONS = {
    ldap.OPT_REFERRALS: 0,
    ldap.OPT_PROTOCOL_VERSION: ldap.VERSION3,
    ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
    ## OPT_X_TLS_NEWCTX doesn't seem to be required:
    # ldap.OPT_X_TLS_NEWCTX: 0,
    ## set OPT_DEBUG_LEVEL to a value bigger than 0 to get debug messages:
    # ldap.OPT_DEBUG_LEVEL: 0,
}
"""@private (skip in pdoc)"""

## NOTE: using the `AUTH_LDAP_CONNECTION_OPTIONS` didn't seem to do the job,
## hence we moved to the `AUTH_LDAP_GLOBAL_OPTIONS` above!
# AUTH_LDAP_CONNECTION_OPTIONS: {
#     ldap.OPT_REFERRALS: 0,
#     # ldap.OPT_PROTOCOL_VERSION: ldap.VERSION3,
#     # ldap.OPT_X_TLS_REQUIRE_CERT: ldap.OPT_X_TLS_ALLOW,
#     # ldap.OPT_X_TLS_NEWCTX: 0,
# }
"""@private (skip in pdoc)"""

AUTH_LDAP_USER_SEARCH = LDAPSearch(
    AUTH_LDAP_BASE_DN,
    ldap.SCOPE_SUBTREE,
    "sAMAccountName=%(user)s",
)
"""@private (skip in pdoc)"""

AUTH_LDAP_USER_ATTR_MAP = {
    "username": "sAMAccountName",
    "first_name": "givenName",
    "last_name": "sn",
    "email": "mail",
}
"""@private (skip in pdoc)"""

# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/

LANGUAGE_CODE = "en-us"
"""@private (skip in pdoc)"""

DATE_FORMAT = "Y-m-d"
"""@private (skip in pdoc)"""

DATETIME_FORMAT = "Y-m-d H:i:s"
"""@private (skip in pdoc)"""

# Disable localized "L10N" formatting of data, otherwise the date and time
# formats defined above will be ignored / overridden.
# NOTE: Django 5.x will always have this set to True, currently it is unclear
# how this will play together with explicitly set date and time formats!
USE_L10N = False
"""@private (skip in pdoc)"""

TIME_ZONE = "Europe/Zurich"
"""@private (skip in pdoc)"""

USE_I18N = True
"""@private (skip in pdoc)"""

USE_TZ = True
"""@private (skip in pdoc)"""


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/


BASE_DIR = Path(__file__).resolve().parent.parent
"""The base directory of the project, i.e. where the `diop` and `diop_core`
directories can be found.

To build paths inside the project, use it like this: `BASE_DIR / 'subdir'`.
"""
"""@private (skip in pdoc)"""


### REVIEW! ###
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
"""The absolute path where `collectstatic` will collect static files for deployment.

https://docs.djangoproject.com/en/4.1/ref/settings/#static-root
"""
"""@private (skip in pdoc)"""


STATICFILES_DIRS = [BASE_DIR / "diop" / "static"]
"""Additional locations the staticfiles app will traverse, e.g. using the
`collectstatic` or `findstatic` management command or through the static file
serving view. Should be set to a list of strings that contain full paths to
the additional files and directories.

https://docs.djangoproject.com/en/4.1/ref/settings/#staticfiles-dirs
"""
"""@private (skip in pdoc)"""


# STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STORAGES = {
    "staticfiles": {
        "BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage",
    },
}
"""@private (skip in pdoc)"""


# Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
"""@private (skip in pdoc)"""

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "django_diop_core_cache_table",
    }
}
"""@private (skip in pdoc)"""

# Email configuration
# https://docs.djangoproject.com/en/4.2/topics/email/
EMAIL_BACKEND = env.str("EMAIL_BACKEND")
EMAIL_HOST = env.str("EMAIL_HOST")


# django-q2 ORM broker connection
Q_CLUSTER = {
    "name": "DjangORM",
    "label": "Task Queue",
    "workers": 1,
    "timeout": 60,
    "retry": 120,
    "catch_up": False,
    "save_limit": 1000,
    "save_limit_per": "func",
    # "queue_limit": 50,
    # "bulk": 10,
    "orm": "default",
    "ALT_CLUSTERS": {
        "statusupdates": {
            "timeout": 30,
            "retry": 99999,
            "max_attempts": 1,
        },
        "housekeeping": {
            "timeout": 900,
            "retry": 99999,
            "workers": 2,
            "max_attempts": 1,
        },
        "activedirectory": {
            "timeout": 300,
            "retry": 600,
            "max_attempts": 4,
        },
        "ppms-short": {
            "timeout": 30,
            "retry": 120,
            "max_attempts": 4,
        },
        "ppms-long": {
            "timeout": 900,
            "retry": 1200,
            "max_attempts": 4,
        },
    },
}
"""@private (skip in pdoc)"""


PYPPMS_API_KEY = env.str("PYPPMS_API_KEY")
PYPPMS_URI = env.str("PYPPMS_URI")
PYPPMS_CACHE = env.str("PYPPMS_CACHE")
PYPPMS_CORE_REF = env.str("PYPPMS_CORE_REF")
PYPPMS_LOC_FILTER = env.str("PYPPMS_LOC_FILTER")
PYPPMS_NAME_GROUPS = env.list("PYPPMS_NAME_GROUPS")


PSYTRICKS_BASE_URL = env.str("PSYTRICKS_BASE_URL", default="http://localhost:8080/")
"""The address where to connect to the ResTricks service.

Used as the `base_url` parameter for the `psytricks.wrapper.ResTricksWrapper`
object. In case not specified, connecting via `localhost` will be attempted.
"""
if not PSYTRICKS_BASE_URL[-1] == "/":
    PSYTRICKS_BASE_URL += "/"

PSYTRICKS_VERIFY = env.bool("PSYTRICKS_VERIFY", default=True)
"""Request validating the ResTricks server version upon connecting.

By setting this to `False`, a mismatch of the server-side version may be
ignored.
"""


DIOP_USER_AD_GROUP = env.str("DIOP_USER_AD_GROUP")
DIOP_SUPPORT_CONTACT = env.str("DIOP_SUPPORT_CONTACT")
DIOP_DISCONNECTED_MAX = env.int("DIOP_DISCONNECTED_MAX")
DIOP_NTDOMAIN = env.str("DIOP_NTDOMAIN")


DIOP_GRACEFUL = env.bool("DIOP_GRACEFUL")
"""When running in "graceful" mode, some tasks operate in a less impacting way.

For example, removing access to a delivery group will be done such that the
standard group will still be allowed to connect.

Most likely not useful in production!
"""


DIOP_DRY_RUN = env.bool("DIOP_DRY_RUN")
"""Running in "dry-run" mode will prevent state-changes to the CVAD platform.

For example changing access permissions, terminating sessions or sending pop-up
messages will NOT be effective, instead only a log message will be issued.

It will NOT affect components fetching status information to update the DB -
they will run normally (as they do not change the CVAD platform). Actions that
are skipped due to dry-run being active will print a warning-level message
to the log instead.
"""
