"""
Django settings for drive project.

Generated by 'django-admin startproject' using Django 3.1.5.

For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""

import os
import tomllib
from socket import gethostbyname, gethostname

from django.utils.translation import gettext_lazy as _

import dj_database_url
import posthog
import sentry_sdk
from boto3.s3.transfer import TransferConfig
from configurations import Configuration, values
from lasuite.configuration.values import SecretFileValue
from sentry_sdk.integrations.django import DjangoIntegration

# pylint: disable=too-many-lines

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = os.environ.get("DATA_DIR", os.path.join("/", "data"))

KB = 1024
MB = 1024 * KB
GB = 1024 * MB


def get_release():
    """
    Get the current release of the application
    """
    try:
        with open(os.path.join(BASE_DIR, "pyproject.toml"), "rb") as f:
            pyproject_data = tomllib.load(f)
        return pyproject_data["project"]["version"]
    except (FileNotFoundError, KeyError):
        return "NA"  # Default: not available


class Base(Configuration):
    """
    This is the base configuration every configuration (aka environment) should inherit from. It
    is recommended to configure third-party applications by creating a configuration mixins in
    ./configurations and compose the Base configuration with those mixins.

    It depends on an environment variable that SHOULD be defined:

    * DJANGO_SECRET_KEY

    You may also want to override default configuration by setting the following environment
    variables:

    * SENTRY_DSN
    * DB_NAME
    * DB_HOST
    * DB_PASSWORD
    * DB_USER
    """

    DEBUG = False
    LOAD_E2E_URLS = False
    USE_SWAGGER = False

    API_VERSION = "v1.0"

    # Security
    ALLOWED_HOSTS = values.ListValue([])
    SECRET_KEY = SecretFileValue(None)

    # Application definition
    ROOT_URLCONF = "drive.urls"
    WSGI_APPLICATION = "drive.wsgi.application"

    # Database
    DATABASES = {
        "default": dj_database_url.config()
        if os.environ.get("DATABASE_URL")
        else {
            "ENGINE": values.Value(
                "django.db.backends.postgresql",
                environ_name="DB_ENGINE",
                environ_prefix=None,
            ),
            "NAME": values.Value("drive", environ_name="DB_NAME", environ_prefix=None),
            "USER": values.Value("dinum", environ_name="DB_USER", environ_prefix=None),
            "PASSWORD": SecretFileValue("pass", environ_name="DB_PASSWORD", environ_prefix=None),
            "HOST": values.Value("localhost", environ_name="DB_HOST", environ_prefix=None),
            "PORT": values.Value(5432, environ_name="DB_PORT", environ_prefix=None),
        }
    }
    DEFAULT_AUTO_FIELD = "django.db.models.AutoField"

    # Search
    SEARCH_INDEXER_CLASS = values.Value(
        default=None,
        environ_name="SEARCH_INDEXER_CLASS",
        environ_prefix=None,
    )
    SEARCH_INDEXER_BATCH_SIZE = values.IntegerValue(
        default=100_000, environ_name="SEARCH_INDEXER_BATCH_SIZE", environ_prefix=None
    )
    SEARCH_INDEXER_URL = values.Value(
        default=None, environ_name="SEARCH_INDEXER_URL", environ_prefix=None
    )
    SEARCH_INDEXER_COUNTDOWN = values.IntegerValue(
        default=1, environ_name="SEARCH_INDEXER_COUNTDOWN", environ_prefix=None
    )
    SEARCH_INDEXER_SECRET = values.Value(
        default=None, environ_name="SEARCH_INDEXER_SECRET", environ_prefix=None
    )
    SEARCH_INDEXER_QUERY_URL = values.Value(
        default=None, environ_name="SEARCH_INDEXER_QUERY_URL", environ_prefix=None
    )
    SEARCH_INDEXER_QUERY_LIMIT = values.PositiveIntegerValue(
        default=50, environ_name="SEARCH_INDEXER_QUERY_LIMIT", environ_prefix=None
    )
    SEARCH_INDEXER_CONTENT_MAX_SIZE = values.PositiveIntegerValue(
        2 * MB,
        environ_name="SEARCH_INDEXER_CONTENT_MAX_SIZE",
        environ_prefix=None,
    )
    SEARCH_INDEXER_ALLOWED_MIMETYPES = values.ListValue(
        ["text/"],
        environ_name="SEARCH_INDEXER_ALLOWED_MIMETYPES",
        environ_prefix=None,
    )

    # Static files (CSS, JavaScript, Images)
    STATIC_URL = "/static/"
    STATIC_ROOT = os.path.join(DATA_DIR, "static")
    MEDIA_URL = "/media/"
    MEDIA_URL_PREVIEW = "/media/preview/"
    MEDIA_ROOT = os.path.join(DATA_DIR, "media")
    MEDIA_BASE_URL = values.Value(None, environ_name="MEDIA_BASE_URL", environ_prefix=None)

    SITE_ID = 1

    STORAGES = {
        "default": {
            "BACKEND": values.Value(
                "storages.backends.s3.S3Storage",
                environ_name="STORAGES_DEFAULT_BACKEND",
                environ_prefix=None,
            ),
            "OPTIONS": {
                "transfer_config": TransferConfig(
                    use_threads=values.BooleanValue(
                        default=True,
                        environ_name="S3_TRANSFER_CONFIG_USE_THREADS",
                        environ_prefix=None,
                    ),
                    multipart_threshold=values.PositiveIntegerValue(
                        default=8 * MB,
                        environ_name="S3_TRANSFER_CONFIG_MULTIPART_THRESHOLD",
                        environ_prefix=None,
                    ),
                    multipart_chunksize=values.PositiveIntegerValue(
                        default=8 * MB,
                        environ_name="S3_TRANSFER_CONFIG_MULTIPART_CHUNKSIZE",
                        environ_prefix=None,
                    ),
                    max_concurrency=values.PositiveIntegerValue(
                        default=10,
                        environ_name="S3_TRANSFER_CONFIG_MAX_CONCURRENCY",
                        environ_prefix=None,
                    ),
                ),
            },
        },
        "staticfiles": {
            "BACKEND": values.Value(
                "whitenoise.storage.CompressedManifestStaticFilesStorage",
                environ_name="STORAGES_STATICFILES_BACKEND",
            ),
        },
    }

    FEATURES_INDEXED_SEARCH = values.BooleanValue(
        default=True, environ_name="FEATURES_INDEXED_SEARCH", environ_prefix=None
    )

    # Posthog
    POSTHOG_KEY = SecretFileValue(None, environ_name="POSTHOG_KEY", environ_prefix=None)
    POSTHOG_HOST = values.Value(
        "https://eu.i.posthog.com", environ_name="POSTHOG_HOST", environ_prefix=None
    )

    # Media
    AWS_S3_ENDPOINT_URL = values.Value(environ_name="AWS_S3_ENDPOINT_URL", environ_prefix=None)
    AWS_S3_ACCESS_KEY_ID = SecretFileValue(environ_name="AWS_S3_ACCESS_KEY_ID", environ_prefix=None)
    AWS_S3_SECRET_ACCESS_KEY = SecretFileValue(
        environ_name="AWS_S3_SECRET_ACCESS_KEY", environ_prefix=None
    )
    AWS_S3_REGION_NAME = values.Value(environ_name="AWS_S3_REGION_NAME", environ_prefix=None)
    AWS_STORAGE_BUCKET_NAME = values.Value(
        "drive-media-storage",
        environ_name="AWS_STORAGE_BUCKET_NAME",
        environ_prefix=None,
    )
    AWS_S3_SIGNATURE_VERSION = values.Value(
        "s3v4",
        environ_name="AWS_S3_SIGNATURE_VERSION",
        environ_prefix=None,
    )
    AWS_S3_UPLOAD_POLICY_EXPIRATION = values.Value(
        60,  # 1 minute
        environ_name="AWS_S3_UPLOAD_POLICY_EXPIRATION",
        environ_prefix=None,
    )
    AWS_S3_DOMAIN_REPLACE = values.Value(
        environ_name="AWS_S3_DOMAIN_REPLACE",
        environ_prefix=None,
    )

    # Maximum size of the request body in memory.
    # This is used to limit the size of the request body in memory.
    # This also limits the size of the file that can be uploaded to the server.
    DATA_UPLOAD_MAX_MEMORY_SIZE = values.PositiveIntegerValue(
        2 * GB,
        environ_name="DATA_UPLOAD_MAX_MEMORY_SIZE",
        environ_prefix=None,
    )
    RESTRICT_UPLOAD_FILE_TYPE = values.BooleanValue(
        default=True,
        environ_name="RESTRICT_UPLOAD_FILE_TYPE",
        environ_prefix=None,
    )
    FILE_EXTENSIONS_ALLOWED = values.ListValue(
        [
            ".001",
            ".002",
            ".003",
            ".3dm",
            ".3dmf",
            ".3g2",
            ".3gp",
            ".7z",
            ".7z.001",
            ".FCStd",
            ".LDR",
            ".N",
            ".SLDDRW",
            ".SLDPRT",
            ".STEP",
            ".aac",
            ".accdb",
            ".acidcsa",
            ".acidppr",
            ".acidsca",
            ".acidssa",
            ".afr",
            ".ai",
            ".aif",
            ".alx",
            ".apafis",
            ".ape",
            ".apk",
            ".arc",
            ".asc",
            ".ase",
            ".asy",
            ".au",
            ".aux",
            ".avi",
            ".axx",
            ".bed",
            ".bib",
            ".bin",
            ".bmp",
            ".bpm",
            ".bpmn",
            ".bz",
            ".bz2",
            ".c",
            ".cdc",
            ".cdxml",
            ".cer",
            ".cif",
            ".clmc",
            ".clmx",
            ".cpg",
            ".crt",
            ".crypto",
            ".csr",
            ".csv",
            ".dat",
            ".dbf",
            ".deb",
            ".dgn",
            ".direct",
            ".djvu",
            ".dna",
            ".doc",
            ".docm",
            ".docx",
            ".docxf",
            ".dot",
            ".dotm",
            ".dotx",
            ".drawio",
            ".drodm",
            ".dsf",
            ".dsn",
            ".dwg",
            ".dwt",
            ".dxf",
            ".dxi",
            ".ecw",
            ".eda",
            ".edi",
            ".egg",
            ".emf",
            ".eml",
            ".ems",
            ".enlx",
            ".eot",
            ".epgz",
            ".epoc",
            ".eps",
            ".epub",
            ".err",
            ".excalidraw",
            ".ext1",
            ".ext2",
            ".ext3",
            ".f0",
            ".f3d",
            ".fas",
            ".fasta",
            ".fcstd",
            ".fic",
            ".fke",
            ".fodg",
            ".fodp",
            ".fods",
            ".fodt",
            ".freeplane",
            ".gan",
            ".gbk",
            ".gcode",
            ".geo",
            ".geojson",
            ".gif",
            ".gml",
            ".gpkg",
            ".gpx",
            ".gradle",
            ".graffle",
            ".graphml",
            ".grist",
            ".gz",
            ".heic",
            ".ico",
            ".ics",
            ".id",
            ".ifc",
            ".igs",
            ".ind",
            ".indd",
            ".int",
            ".ipynb",
            ".jif",
            ".jp2",
            ".jpe",
            ".jpeg",
            ".jpg",
            ".json",
            ".kdbx",
            ".key",
            ".kml",
            ".kmz",
            ".layout",
            ".loc",
            ".log",
            ".lss",
            ".lst",
            ".m",
            ".m4a",
            ".m4b",
            ".m4p",
            ".m4r",
            ".m4v",
            ".map",
            ".mbz",
            ".mcd",
            ".md",
            ".mdb",
            ".mkv",
            ".mm",
            ".mmap",
            ".moo",
            ".mov",
            ".mp3",
            ".mp4",
            ".mpd",
            ".mpeg",
            ".mpga",
            ".mph",
            ".mpp",
            ".mri",
            ".mrk",
            ".msg",
            ".mtgl",
            ".mts",
            ".mtz",
            ".mvdx",
            ".naf",
            ".nist",
            ".nitrace",
            ".numbers",
            ".oaf",
            ".obj",
            ".odb",
            ".odc",
            ".odf",
            ".odg",
            ".odi",
            ".odm",
            ".odp",
            ".ods",
            ".odt",
            ".oform",
            ".oft",
            ".one",
            ".opj",
            ".opju",
            ".osf",
            ".otc",
            ".otf",
            ".otg",
            ".oth",
            ".oti",
            ".oto",
            ".otp",
            ".ots",
            ".ott",
            ".p7s",
            ".pages",
            ".pbix",
            ".pdb",
            ".pdf",
            ".pdfxml",
            ".pgw",
            ".ply",
            ".png",
            ".pot",
            ".potm",
            ".potx",
            ".pps",
            ".ppsm",
            ".ppsx",
            ".ppt",
            ".pptm",
            ".pptx",
            ".pr1",
            ".pr2",
            ".prism",
            ".prj",
            ".properties",
            ".proto",
            ".prproj",
            ".psd",
            ".pub",
            ".puw",
            ".py",
            ".pyc",
            ".pzfx",
            ".qgr",
            ".qgs",
            ".qgz",
            ".qix",
            ".qmd",
            ".qml",
            ".qpj",
            ".qpt",
            ".r",
            ".rar",
            ".rdata",
            ".rds",
            ".rep",
            ".ris",
            ".rmd",
            ".rtf",
            ".rvt",
            ".sb2",
            ".sb3",
            ".sbn",
            ".sbx",
            ".scwsp",
            ".sh",
            ".shp",
            ".shx",
            ".sig",
            ".skp",
            ".sldasm",
            ".slddrw",
            ".slk",
            ".sphd",
            ".sphx",
            ".sql",
            ".srt",
            ".ssp",
            ".stc",
            ".std",
            ".stl",
            ".stp",
            ".stw",
            ".svg",
            ".swu",
            ".sxc",
            ".sxd",
            ".sxg",
            ".sxi",
            ".sxm",
            ".sxw",
            ".tab",
            ".tar",
            ".tex",
            ".text",
            ".textgrid",
            ".tfw",
            ".tif",
            ".tiff",
            ".tsv",
            ".ttc",
            ".ttf",
            ".twbx",
            ".txt",
            ".vcf",
            ".vsd",
            ".vsdx",
            ".vtt",
            ".vuw",
            ".wa1",
            ".wav",
            ".weba",
            ".webm",
            ".webp",
            ".wfl",
            ".woff",
            ".woff2",
            ".wsp",
            ".xcf",
            ".xd",
            ".xhl",
            ".xls",
            ".xlsb",
            ".xlsm",
            ".xlsx",
            ".xlt",
            ".xltm",
            ".xltx",
            ".xmind",
            ".xml",
            ".xps",
            ".xsd",
            ".xz",
            ".yml",
            ".zed",
            ".zip",
        ],
        environ_name="FILE_EXTENSIONS_ALLOWED",
        environ_prefix=None,
    )

    FILE_MIMETYPE_ALLOWED = values.ListValue(
        [
            "application/epub+zip",
            "application/gml+xml",
            "application/gpx+xml",
            "application/gzip",
            "application/json",
            "application/msword",
            "application/pdf",
            "application/pgp-signature",
            "application/pkcs10",
            "application/pkcs7-signature",
            "application/pkix-cert",
            "application/postscript",
            "application/rtf",
            "application/sql",
            "application/vnd.android.package-archive",
            "application/vnd.businessobjects",
            "application/vnd.chemdraw+xml",
            "application/vnd.debian.binary-package",
            "application/vnd.dna",
            "application/vnd.dynageo",
            "application/vnd.google-earth.kml+xml",
            "application/vnd.google-earth.kmz",
            "application/vnd.koan",
            "application/vnd.mcd",
            "application/vnd.mindjet.mindmanager",
            "application/vnd.ms-excel",
            "application/vnd.ms-excel.sheet.binary.macroenabled.12",
            "application/vnd.ms-excel.sheet.macroenabled.12",
            "application/vnd.ms-excel.template.macroenabled.12",
            "application/vnd.ms-fontobject",
            "application/vnd.ms-outlook",
            "application/vnd.ms-pki.stl",
            "application/vnd.ms-powerpoint",
            "application/vnd.ms-powerpoint.presentation.macroenabled.12",
            "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
            "application/vnd.ms-powerpoint.template.macroenabled.12",
            "application/vnd.ms-project",
            "application/vnd.ms-word.document.macroenabled.12",
            "application/vnd.ms-word.template.macroenabled.12",
            "application/vnd.ms-xpsdocument",
            "application/vnd.oasis.opendocument.chart",
            "application/vnd.oasis.opendocument.chart-template",
            "application/vnd.oasis.opendocument.database",
            "application/vnd.oasis.opendocument.formula",
            "application/vnd.oasis.opendocument.graphics",
            "application/vnd.oasis.opendocument.graphics-template",
            "application/vnd.oasis.opendocument.image",
            "application/vnd.oasis.opendocument.image-template",
            "application/vnd.oasis.opendocument.presentation",
            "application/vnd.oasis.opendocument.presentation-template",
            "application/vnd.oasis.opendocument.spreadsheet",
            "application/vnd.oasis.opendocument.spreadsheet-template",
            "application/vnd.oasis.opendocument.text",
            "application/vnd.oasis.opendocument.text-master",
            "application/vnd.oasis.opendocument.text-template",
            "application/vnd.oasis.opendocument.text-web",
            "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
            "application/vnd.openxmlformats-officedocument.presentationml.template",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
            "application/vnd.palm",
            "application/vnd.realvnc.bed",
            "application/vnd.sqlite3",
            "application/vnd.sun.xml.calc",
            "application/vnd.sun.xml.calc.template",
            "application/vnd.sun.xml.draw",
            "application/vnd.sun.xml.draw.template",
            "application/vnd.sun.xml.impress",
            "application/vnd.sun.xml.math",
            "application/vnd.sun.xml.writer",
            "application/vnd.sun.xml.writer.global",
            "application/vnd.sun.xml.writer.template",
            "application/vnd.visio",
            "application/vnd.yamaha.openscoreformat",
            "application/x-7z-compressed",
            "application/x-bzip",
            "application/x-bzip2",
            "application/x-debian-package",
            "application/x-empty",
            "application/x-freearc",
            "application/x-keepass",
            "application/x-keepass2",
            "application/x-msaccess",
            "application/x-msmetafile",
            "application/x-mspublisher",
            "application/x-python-bytecodeapplication/x-shellscript",
            "application/x-python-code",
            "application/x-rar",
            "application/x-rar-compressed",
            "application/x-research-info-systems",
            "application/x-sh",
            "application/x-sqlite3",
            "application/x-sql",
            "application/x-subrip",
            "application/x-tar",
            "application/x-tex",
            "application/x-tgif",
            "application/x-x509-ca-cert",
            "application/x-xz",
            "application/xml",
            "application/yaml",
            "application/zed",
            "application/zip",
            "audio/aac",
            "audio/basic",
            "audio/mp4",
            "audio/mpeg",
            "audio/wav",
            "audio/webm",
            "audio/x-aac",
            "audio/x-aiff",
            "audio/x-wav",
            "chemical/x-cif",
            "font/collection",
            "font/otf",
            "font/ttf",
            "font/woff",
            "font/woff2",
            "image/bmp",
            "image/gif",
            "image/heic",
            "image/jpeg",
            "image/png",
            "image/svg+xml",
            "image/tiff",
            "image/vnd.adobe.photoshop",
            "image/vnd.djvu",
            "image/vnd.dwg",
            "image/vnd.dxf",
            "image/webp",
            "image/x-icon",
            "message/rfc822",
            "model/iges",
            "text/calendar",
            "text/csv",
            "text/markdown",
            "text/plain",
            "text/tab-separated-values",
            "text/vtt",
            "text/x-c",
            "text/x-python",
            "text/x-tex",
            "text/x-vcard",
            "video/3gpp",
            "video/3gpp2",
            "video/mp2t",
            "video/mp4",
            "video/mpeg",
            "video/quicktime",
            "video/webm",
            "video/x-m4v",
            "video/x-matroska",
            "video/x-msvideo",
            # Fallback
            "application/octet-stream",
        ],
        environ_name="FILE_MIMETYPE_ALLOWED",
        environt_prefix=None,
    )

    ITEM_PREVIEWABLE_MIME_TYPES = values.ListValue(
        [
            "image/",
            "video/",
            "audio/",
            "application/pdf",
        ],
        environ_name="ITEM_PREVIEWABLE_MIME_TYPES",
        environ_prefix=None,
    )

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

    # Languages
    LANGUAGE_CODE = values.Value("en-us")
    LANGUAGE_COOKIE_NAME = "drive_language"  # cookie & language is set from frontend

    DRF_NESTED_MULTIPART_PARSER = {
        # output of parser is converted to querydict
        # if is set to False, dict python is returned
        "querydict": False,
    }

    # Careful! Languages should be ordered by priority, as this tuple is used to get
    # fallback/default languages throughout the app.
    LANGUAGES = values.SingleNestedTupleValue(
        (
            ("en-us", _("English")),
            ("fr-fr", _("French")),
            ("de-de", _("German")),
            ("nl-nl", _("Dutch")),
        )
    )

    LOCALE_PATHS = (os.path.join(BASE_DIR, "locale"),)

    TIME_ZONE = "UTC"
    USE_I18N = True
    USE_TZ = True

    # Templates
    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "DIRS": [os.path.join(BASE_DIR, "templates")],
            "OPTIONS": {
                "context_processors": [
                    "django.contrib.auth.context_processors.auth",
                    "django.contrib.messages.context_processors.messages",
                    "django.template.context_processors.csrf",
                    "django.template.context_processors.debug",
                    "django.template.context_processors.i18n",
                    "django.template.context_processors.media",
                    "django.template.context_processors.request",
                    "django.template.context_processors.tz",
                ],
                "loaders": [
                    "django.template.loaders.filesystem.Loader",
                    "django.template.loaders.app_directories.Loader",
                ],
            },
        },
    ]

    MIDDLEWARE = [
        "django.middleware.security.SecurityMiddleware",
        "whitenoise.middleware.WhiteNoiseMiddleware",
        "django.contrib.sessions.middleware.SessionMiddleware",
        "django.middleware.locale.LocaleMiddleware",
        "django.middleware.clickjacking.XFrameOptionsMiddleware",
        "corsheaders.middleware.CorsMiddleware",
        "django.middleware.common.CommonMiddleware",
        "django.middleware.csrf.CsrfViewMiddleware",
        "django.contrib.auth.middleware.AuthenticationMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "dockerflow.django.middleware.DockerflowMiddleware",
    ]

    AUTHENTICATION_BACKENDS = [
        "django.contrib.auth.backends.ModelBackend",
        "core.authentication.backends.OIDCAuthenticationBackend",
    ]

    # Django applications from the highest priority to the lowest
    INSTALLED_APPS = [
        "core",
        "wopi",
        "drf_spectacular",
        "drf_standardized_errors",
        # Third party apps
        "corsheaders",
        "django_filters",
        "dockerflow.django",
        "rest_framework",
        "rest_framework_api_key",
        "parler",
        "django_ltree",
        "easy_thumbnails",
        # Django
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.postgres",
        "django.contrib.sessions",
        "django.contrib.sites",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        # OIDC third party
        "mozilla_django_oidc",
        "lasuite.malware_detection",
    ]

    # Cache
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": values.Value(
                "redis://redis:6379/0",
                environ_name="REDIS_URL",
                environ_prefix=None,
            ),
            "TIMEOUT": values.IntegerValue(
                30,  # timeout in seconds
                environ_name="CACHES_DEFAULT_TIMEOUT",
                environ_prefix=None,
            ),
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
            "KEY_PREFIX": values.Value(
                "drive",
                environ_name="CACHES_DEFAULT_KEY_PREFIX",
                environ_prefix=None,
            ),
        },
        "session": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": values.Value(
                "redis://redis:6379/0",
                environ_name="REDIS_URL",
                environ_prefix=None,
            ),
            "TIMEOUT": values.IntegerValue(
                30,  # timeout in seconds
                environ_name="CACHES_SESSION_TIMEOUT",
                environ_prefix=None,
            ),
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
        },
    }

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            "mozilla_django_oidc.contrib.drf.OIDCAuthentication",
            "rest_framework.authentication.SessionAuthentication",
        ),
        "DEFAULT_PARSER_CLASSES": [
            "rest_framework.parsers.JSONParser",
            "nested_multipart_parser.drf.DrfNestedParser",
        ],
        "DEFAULT_RENDERER_CLASSES": [
            # 🔒️ Disable BrowsableAPIRenderer which provides forms allowing a user to
            # see all the data in the database (ie a serializer with a ForeignKey field
            # will generate a form with a field with all possible values of the FK).
            "rest_framework.renderers.JSONRenderer",
        ],
        "EXCEPTION_HANDLER": "core.api.exception_handler",
        "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
        "PAGE_SIZE": 20,
        "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
        "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
        "DEFAULT_THROTTLE_CLASSES": ["rest_framework.throttling.ScopedRateThrottle"],
        "DEFAULT_THROTTLE_RATES": {
            "user_list_sustained": values.Value(
                default="180/hour",
                environ_name="API_USERS_LIST_THROTTLE_RATE_SUSTAINED",
                environ_prefix=None,
            ),
            "user_list_burst": values.Value(
                default="30/minute",
                environ_name="API_USERS_LIST_THROTTLE_RATE_BURST",
                environ_prefix=None,
            ),
            "sdk_event_relay": values.Value(
                default="200/minute",
                environ_name="API_SDK_EVENT_RELAY_THROTTLE_RATE",
                environ_prefix=None,
            ),
        },
    }

    MAX_PAGE_SIZE = values.PositiveIntegerValue(
        200, environ_name="MAX_PAGE_SIZE", environ_prefix=None
    )

    SPECTACULAR_SETTINGS = {
        "TITLE": "drive API",
        "DESCRIPTION": "This is the drive API schema.",
        "VERSION": "1.0.0",
        "SERVE_INCLUDE_SCHEMA": False,
        "ENABLE_DJANGO_DEPLOY_CHECK": values.BooleanValue(
            default=False,
            environ_name="SPECTACULAR_SETTINGS_ENABLE_DJANGO_DEPLOY_CHECK",
        ),
        "COMPONENT_SPLIT_REQUEST": True,
        # OTHER SETTINGS
        "SWAGGER_UI_DIST": "SIDECAR",  # shorthand to use the sidecar instead
        "SWAGGER_UI_FAVICON_HREF": "SIDECAR",
        "REDOC_DIST": "SIDECAR",
    }

    TRASHBIN_CUTOFF_DAYS = values.Value(
        30, environ_name="TRASHBIN_CUTOFF_DAYS", environ_prefix=None
    )
    PURGE_GRACE_DAYS = values.Value(7, environ_name="PURGE_GRACE_DAYS", environ_prefix=None)

    # Mail
    EMAIL_BACKEND = values.Value("django.core.mail.backends.smtp.EmailBackend")
    EMAIL_BRAND_NAME = values.Value(None)
    EMAIL_HOST = values.Value(None)
    EMAIL_HOST_USER = values.Value(None)
    EMAIL_HOST_PASSWORD = SecretFileValue(None)
    EMAIL_LOGO_IMG = values.Value(None)
    EMAIL_PORT = values.PositiveIntegerValue(None)
    EMAIL_URL_APP = values.Value(None)
    EMAIL_USE_TLS = values.BooleanValue(False)
    EMAIL_USE_SSL = values.BooleanValue(False)
    EMAIL_FROM = values.Value("from@example.com")

    # User accounts reconciliation
    USER_RECONCILIATION_FORM_URL = values.Value(
        None, environ_name="USER_RECONCILIATION_FORM_URL", environ_prefix=None
    )

    AUTH_USER_MODEL = "core.User"
    INVITATION_VALIDITY_DURATION = values.PositiveIntegerValue(
        604800, environ_name="INVITATION_VALIDITY_DURATION", environ_prefix=None
    )  # 7 days, in seconds

    # CORS
    CORS_ALLOW_CREDENTIALS = True
    CORS_ALLOW_ALL_ORIGINS = values.BooleanValue(False)
    CORS_ALLOWED_ORIGINS = values.ListValue([])
    CORS_ALLOWED_ORIGIN_REGEXES = values.ListValue([])

    # Sentry
    SENTRY_DSN = values.Value(None, environ_name="SENTRY_DSN", environ_prefix=None)

    # Frontend
    FRONTEND_THEME = values.Value(None, environ_name="FRONTEND_THEME", environ_prefix=None)
    FRONTEND_MORE_LINK = values.Value(
        None,
        environ_name="FRONTEND_MORE_LINK",
        environ_prefix=None,
    )
    FRONTEND_FEEDBACK_BUTTON_SHOW = values.BooleanValue(
        default=False, environ_name="FRONTEND_FEEDBACK_BUTTON_SHOW", environ_prefix=None
    )
    # For instance, you might want to bind this button to an external library like
    # Posthog to trigger survey instead of the build in feedback modal.
    FRONTEND_FEEDBACK_BUTTON_IDLE = values.BooleanValue(
        default=False, environ_name="FRONTEND_FEEDBACK_BUTTON_IDLE", environ_prefix=None
    )
    FRONTEND_FEEDBACK_ITEMS = values.DictValue(
        {}, environ_name="FRONTEND_FEEDBACK_ITEMS", environ_prefix=None
    )
    FRONTEND_FEEDBACK_MESSAGES_WIDGET_ENABLED = values.BooleanValue(
        default=False,
        environ_name="FRONTEND_FEEDBACK_MESSAGES_WIDGET_ENABLED",
        environ_prefix=None,
    )
    FRONTEND_FEEDBACK_MESSAGES_WIDGET_API_URL = values.Value(
        None,
        environ_name="FRONTEND_FEEDBACK_MESSAGES_WIDGET_API_URL",
        environ_prefix=None,
    )
    FRONTEND_FEEDBACK_MESSAGES_WIDGET_CHANNEL = values.Value(
        None,
        environ_name="FRONTEND_FEEDBACK_MESSAGES_WIDGET_CHANNEL",
        environ_prefix=None,
    )
    FRONTEND_FEEDBACK_MESSAGES_WIDGET_PATH = values.Value(
        None, environ_name="FRONTEND_FEEDBACK_MESSAGES_WIDGET_PATH", environ_prefix=None
    )
    FRONTEND_HIDE_GAUFRE = values.BooleanValue(
        default=False, environ_name="FRONTEND_HIDE_GAUFRE", environ_prefix=None
    )
    FRONTEND_SILENT_LOGIN_ENABLED = values.BooleanValue(
        default=False, environ_name="FRONTEND_SILENT_LOGIN_ENABLED", environ_prefix=None
    )
    FRONTEND_EXTERNAL_HOME_URL = values.Value(
        None, environ_name="FRONTEND_EXTERNAL_HOME_URL", environ_prefix=None
    )
    FRONTEND_RELEASE_NOTE_ENABLED = values.BooleanValue(
        default=True, environ_name="FRONTEND_RELEASE_NOTE_ENABLED", environ_prefix=None
    )
    FRONTEND_ENTITLEMENTS_DISCLAIMERS = values.DictValue(
        {}, environ_name="FRONTEND_ENTITLEMENTS_DISCLAIMERS", environ_prefix=None
    )
    FRONTEND_CSS_URL = values.Value(None, environ_name="FRONTEND_CSS_URL", environ_prefix=None)
    FRONTEND_JS_URL = values.Value(None, environ_name="FRONTEND_JS_URL", environ_prefix=None)
    THEME_CUSTOMIZATION_FILE_PATH = values.Value(
        os.path.join(BASE_DIR, "drive/configuration/theme/default.json"),
        environ_name="THEME_CUSTOMIZATION_FILE_PATH",
        environ_prefix=None,
    )
    THEME_CUSTOMIZATION_CACHE_TIMEOUT = values.Value(
        60 * 60 * 24,
        environ_name="THEME_CUSTOMIZATION_CACHE_TIMEOUT",
        environ_prefix=None,
    )
    # Crisp
    CRISP_WEBSITE_ID = values.Value(None, environ_name="CRISP_WEBSITE_ID", environ_prefix=None)

    # Easy thumbnails
    THUMBNAIL_EXTENSION = "webp"
    THUMBNAIL_TRANSPARENCY_EXTENSION = "webp"
    THUMBNAIL_DEFAULT_STORAGE_ALIAS = "default"
    THUMBNAIL_ALIASES = {}

    # Celery
    CELERY_BROKER_URL = values.Value("redis://redis:6379/0")
    CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({})
    CELERY_TASK_ROUTES = values.DictValue({})

    # Session
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "session"
    SESSION_COOKIE_AGE = 60 * 60 * 12

    # OIDC - Authorization Code Flow
    OIDC_CREATE_USER = values.BooleanValue(
        default=True,
        environ_name="OIDC_CREATE_USER",
    )
    OIDC_AUTHENTICATE_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationRequestView"
    OIDC_CALLBACK_CLASS = "core.authentication.views.OIDCAuthenticationCallbackView"
    OIDC_RP_SIGN_ALGO = values.Value("RS256", environ_name="OIDC_RP_SIGN_ALGO", environ_prefix=None)
    OIDC_RP_CLIENT_ID = values.Value("drive", environ_name="OIDC_RP_CLIENT_ID", environ_prefix=None)
    OIDC_RP_CLIENT_SECRET = SecretFileValue(
        None,
        environ_name="OIDC_RP_CLIENT_SECRET",
        environ_prefix=None,
    )
    OIDC_OP_JWKS_ENDPOINT = values.Value(environ_name="OIDC_OP_JWKS_ENDPOINT", environ_prefix=None)
    OIDC_OP_AUTHORIZATION_ENDPOINT = values.Value(
        environ_name="OIDC_OP_AUTHORIZATION_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_TOKEN_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_TOKEN_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_USER_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_USER_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_LOGOUT_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_LOGOUT_ENDPOINT", environ_prefix=None
    )
    OIDC_REDIRECT_FIELD_NAME = values.Value(
        "returnTo", environ_name="OIDC_REDIRECT_FIELD_NAME", environ_prefix=None
    )
    OIDC_AUTH_REQUEST_EXTRA_PARAMS = values.DictValue(
        {}, environ_name="OIDC_AUTH_REQUEST_EXTRA_PARAMS", environ_prefix=None
    )
    OIDC_RP_SCOPES = values.Value(
        "openid email", environ_name="OIDC_RP_SCOPES", environ_prefix=None
    )
    LOGIN_REDIRECT_URL = values.Value(None, environ_name="LOGIN_REDIRECT_URL", environ_prefix=None)
    LOGIN_REDIRECT_URL_FAILURE = values.Value(
        None, environ_name="LOGIN_REDIRECT_URL_FAILURE", environ_prefix=None
    )
    LOGOUT_REDIRECT_URL = values.Value(
        None, environ_name="LOGOUT_REDIRECT_URL", environ_prefix=None
    )
    OIDC_USE_NONCE = values.BooleanValue(
        default=True, environ_name="OIDC_USE_NONCE", environ_prefix=None
    )
    OIDC_REDIRECT_REQUIRE_HTTPS = values.BooleanValue(
        default=False, environ_name="OIDC_REDIRECT_REQUIRE_HTTPS", environ_prefix=None
    )
    OIDC_REDIRECT_ALLOWED_HOSTS = values.ListValue(
        default=[], environ_name="OIDC_REDIRECT_ALLOWED_HOSTS", environ_prefix=None
    )
    OIDC_STORE_ID_TOKEN = values.BooleanValue(
        default=True, environ_name="OIDC_STORE_ID_TOKEN", environ_prefix=None
    )
    OIDC_USE_PKCE = values.BooleanValue(
        default=False, environ_name="OIDC_USE_PKCE", environ_prefix=None
    )
    OIDC_PKCE_CODE_CHALLENGE_METHOD = values.Value(
        default="S256",
        environ_name="OIDC_PKCE_CODE_CHALLENGE_METHOD",
        environ_prefix=None,
    )
    OIDC_PKCE_CODE_VERIFIER_SIZE = values.IntegerValue(
        default=64, environ_name="OIDC_PKCE_CODE_VERIFIER_SIZE", environ_prefix=None
    )
    OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = values.BooleanValue(
        default=True,
        environ_name="OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION",
        environ_prefix=None,
    )

    OIDC_STORE_ACCESS_TOKEN = values.BooleanValue(
        default=False, environ_name="OIDC_STORE_ACCESS_TOKEN", environ_prefix=None
    )
    OIDC_STORE_REFRESH_TOKEN = values.BooleanValue(
        default=False, environ_name="OIDC_STORE_REFRESH_TOKEN", environ_prefix=None
    )
    OIDC_STORE_REFRESH_TOKEN_KEY = values.Value(
        default=None,
        environ_name="OIDC_STORE_REFRESH_TOKEN_KEY",
        environ_prefix=None,
    )

    # OIDC claims to store
    OIDC_STORE_CLAIMS = values.ListValue(
        default=[],
        environ_name="OIDC_STORE_CLAIMS",
        environ_prefix=None,
    )

    # WARNING: Enabling this setting allows multiple user accounts to share the same email
    # address. This may cause security issues and is not recommended for production use when
    # email is activated as fallback for identification (see previous setting).
    OIDC_ALLOW_DUPLICATE_EMAILS = values.BooleanValue(
        default=False,
        environ_name="OIDC_ALLOW_DUPLICATE_EMAILS",
        environ_prefix=None,
    )

    OIDC_USER_INFO = values.ListValue(
        default=values.ListValue(  # retrocompatibility
            default=[],
            environ_name="USER_OIDC_ESSENTIAL_CLAIMS",
            environ_prefix=None,
        ),
        environ_name="OIDC_USER_INFO",
        environ_prefix=None,
    )

    OIDC_USERINFO_FULLNAME_FIELDS = values.ListValue(
        default=["first_name", "last_name"],
        environ_name="OIDC_USERINFO_FULLNAME_FIELDS",
        environ_prefix=None,
    )
    OIDC_USERINFO_SHORTNAME_FIELD = values.Value(
        default="first_name",
        environ_name="OIDC_USERINFO_SHORTNAME_FIELD",
        environ_prefix=None,
    )

    # OIDC Resource Server

    OIDC_RESOURCE_SERVER_ENABLED = values.BooleanValue(
        default=False, environ_name="OIDC_RESOURCE_SERVER_ENABLED", environ_prefix=None
    )

    OIDC_RS_BACKEND_CLASS = values.Value(
        "lasuite.oidc_resource_server.backend.ResourceServerBackend",
        environ_name="OIDC_RS_BACKEND_CLASS",
        environ_prefix=None,
    )

    OIDC_OP_URL = values.Value(None, environ_name="OIDC_OP_URL", environ_prefix=None)

    OIDC_VERIFY_SSL = values.BooleanValue(
        default=True, environ_name="OIDC_VERIFY_SSL", environ_prefix=None
    )

    OIDC_TIMEOUT = values.PositiveIntegerValue(3, environ_name="OIDC_TIMEOUT", environ_prefix=None)

    OIDC_PROXY = values.Value(None, environ_name="OIDC_PROXY", environ_prefix=None)

    OIDC_OP_INTROSPECTION_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_INTROSPECTION_ENDPOINT", environ_prefix=None
    )

    OIDC_RS_CLIENT_ID = values.Value(None, environ_name="OIDC_RS_CLIENT_ID", environ_prefix=None)

    OIDC_RS_CLIENT_SECRET = values.Value(
        None, environ_name="OIDC_RS_CLIENT_SECRET", environ_prefix=None
    )

    OIDC_RS_AUDIENCE_CLAIM = values.Value(
        "client_id", environ_name="OIDC_RS_AUDIENCE_CLAIM", environ_prefix=None
    )

    OIDC_RS_ENCRYPTION_ENCODING = values.Value(
        "A256GCM", environ_name="OIDC_RS_ENCRYPTION_ENCODING", environ_prefix=None
    )

    OIDC_RS_ENCRYPTION_ALGO = values.Value(
        "RSA-OAEP", environ_name="OIDC_RS_ENCRYPTION_ALGO", environ_prefix=None
    )

    OIDC_RS_SIGNING_ALGO = values.Value(
        "ES256", environ_name="OIDC_RS_SIGNING_ALGO", environ_prefix=None
    )

    OIDC_RS_SCOPES = values.ListValue(
        ["openid"], environ_name="OIDC_RS_SCOPES", environ_prefix=None
    )

    OIDC_RS_ALLOWED_AUDIENCES = values.ListValue(
        default=[],
        environ_name="OIDC_RS_ALLOWED_AUDIENCES",
        environ_prefix=None,
    )

    # External API Configuration
    # Configure available routes and actions for external_api endpoints
    EXTERNAL_API = values.DictValue(
        default={
            "items": {
                "enabled": True,
                "actions": ["create", "list", "retrieve", "children", "upload_ended"],
            },
            "item_access": {
                "enabled": False,
                "actions": [],
            },
            "item_invitation": {
                "enabled": False,
                "actions": [],
            },
            "users": {
                "enabled": True,
                "actions": ["get_me"],
            },
        },
        environ_name="EXTERNAL_API",
        environ_prefix=None,
    )

    OIDC_RS_PRIVATE_KEY_STR = values.Value(
        default=None,
        environ_name="OIDC_RS_PRIVATE_KEY_STR",
        environ_prefix=None,
    )
    OIDC_RS_ENCRYPTION_KEY_TYPE = values.Value(
        default="RSA",
        environ_name="OIDC_RS_ENCRYPTION_KEY_TYPE",
        environ_prefix=None,
    )

    # SDK Relay
    SDK_RELAY_CACHE_TIMEOUT = values.PositiveIntegerValue(
        600,  # 10 minutes
        environ_name="SDK_RELAY_CACHE_TIMEOUT",
        environ_prefix=None,
    )
    SDK_CORS_ALLOWED_ORIGINS = values.ListValue(
        default=[],
        environ_name="SDK_CORS_ALLOWED_ORIGINS",
        environ_prefix=None,
    )

    ALLOW_LOGOUT_GET_METHOD = values.BooleanValue(
        default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None
    )

    # Logging
    # We want to make it easy to log to console but by default we log production
    # to Sentry and don't want to log to console.
    LOGGING = {
        "version": 1,
        "disable_existing_loggers": False,
        "formatters": {
            "simple": {
                "format": "{asctime} {name} {levelname} {message}",
                "style": "{",
            },
        },
        "handlers": {
            "console": {
                "class": "logging.StreamHandler",
                "formatter": "simple",
            },
        },
        # Override root logger to send it to console
        "root": {
            "handlers": ["console"],
            "level": values.Value(
                "INFO", environ_name="LOGGING_LEVEL_LOGGERS_ROOT", environ_prefix=None
            ),
        },
        "loggers": {
            "core": {
                "handlers": ["console"],
                "level": values.Value(
                    "INFO",
                    environ_name="LOGGING_LEVEL_LOGGERS_APP",
                    environ_prefix=None,
                ),
                "propagate": True,
            },
        },
    }

    API_USERS_LIST_LIMIT = values.PositiveIntegerValue(
        default=5,
        environ_name="API_USERS_LIST_LIMIT",
        environ_prefix=None,
    )
    # WOPI

    # WOPI_CLIENTS contains a list of client name. These client names will be used in the post_setup
    # to configure the settings related to the client.
    # Example :
    # WOPI_CLIENTS = ["vendorA"]
    # Then these settings will be cheked in the post_setup:
    # WOPI_VENDORA_DISCOVERY_URL = https://vendorA.com/hosting/discovery
    # If they are missing, a ValueError will be raised.
    WOPI_CLIENTS = values.ListValue([], environ_name="WOPI_CLIENTS", environ_prefix=None)
    WOPI_CLIENTS_CONFIGURATION = {}
    WOPI_EXCLUDED_MIMETYPES = values.ListValue(
        [
            "image/svg+xml",
            "image/png",
            "image/jpg",
            "image/jpeg",
            "image/gif",
            "image/bmp",
            "image/tiff",
            "image/webp",
            "application/pdf",
            "image/x-wpg",
            "image/cgm",
            "image/vnd.dxf",
            "image/x-emf",
            "image/x-wmf",
            "image/x-freehand",
        ],
        environ_name="WOPI_EXCLUDED_MIMETYPES",
        environ_prefix=None,
    )
    WOPI_EXCLUDED_EXTENSIONS = values.ListValue(
        ["svg", "png", "jpg", "jpeg", "gif", "bmp", "tiff", "webp", "pdf"],
        environ_name="WOPI_EXCLUDED_EXTENSIONS",
        environ_prefix=None,
    )
    WOPI_CONFIGURATION_CACHE_EXPIRATION = values.IntegerValue(
        60 * 60 * 24,
        environ_name="WOPI_CONFIGURATION_CACHE_EXPIRATION",
        environ_prefix=None,
    )
    WOPI_SRC_BASE_URL = values.Value(
        None,
        environ_name="WOPI_SRC_BASE_URL",
        environ_prefix=None,
    )

    # We recommend using an access_token_ttl value that makes the access token valid for 10 hours.
    WOPI_ACCESS_TOKEN_TIMEOUT = values.IntegerValue(
        60 * 60 * 10, environ_name="WOPI_ACCESS_TOKEN_TIMEOUT", environ_prefix=None
    )
    WOPI_CONVERSION_SOURCE_TOKEN_TIMEOUT = values.IntegerValue(
        120, environ_name="WOPI_CONVERSION_SOURCE_TOKEN_TIMEOUT", environ_prefix=None
    )
    WOPI_LOCK_TIMEOUT = values.IntegerValue(
        30 * 60, environ_name="WOPI_LOCK_TIMEOUT", environ_prefix=None
    )
    WOPI_LEGACY_CONVERSION_TARGETS = {
        "doc": "docx",
        "xls": "xlsx",
        "ppt": "pptx",
    }
    WOPI_ONLYOFFICE_CONVERT_HTTP_CONNECT_TIMEOUT = values.IntegerValue(
        5, environ_name="WOPI_ONLYOFFICE_CONVERT_HTTP_CONNECT_TIMEOUT", environ_prefix=None
    )
    WOPI_ONLYOFFICE_CONVERT_HTTP_READ_TIMEOUT = values.IntegerValue(
        60, environ_name="WOPI_ONLYOFFICE_CONVERT_HTTP_READ_TIMEOUT", environ_prefix=None
    )
    WOPI_ONLYOFFICE_CONVERT_DOWNLOAD_CONNECT_TIMEOUT = values.IntegerValue(
        5, environ_name="WOPI_ONLYOFFICE_CONVERT_DOWNLOAD_CONNECT_TIMEOUT", environ_prefix=None
    )
    WOPI_ONLYOFFICE_CONVERT_DOWNLOAD_READ_TIMEOUT = values.IntegerValue(
        30, environ_name="WOPI_ONLYOFFICE_CONVERT_DOWNLOAD_READ_TIMEOUT", environ_prefix=None
    )
    WOPI_ONLYOFFICE_CONVERT_JWT_SECRET = SecretFileValue(
        None, environ_name="WOPI_ONLYOFFICE_CONVERT_JWT_SECRET", environ_prefix=None
    )
    WOPI_DISABLE_CHAT = values.IntegerValue(
        0, environ_name="WOPI_DISABLE_CHAT", environ_prefix=None
    )

    WOPI_CONFIGURATION_CRONTAB_MINUTE = values.Value(
        0, environ_name="WOPI_CONFIGURATION_CRONTAB_MINUTE", environ_prefix=None
    )
    WOPI_CONFIGURATION_CRONTAB_HOUR = values.Value(
        3, environ_name="WOPI_CONFIGURATION_CRONTAB_HOUR", environ_prefix=None
    )
    WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH = values.Value(
        "*", environ_name="WOPI_CONFIGURATION_CRONTAB_DAY_OF_MONTH", environ_prefix=None
    )
    WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR = values.Value(
        "*",
        environ_name="WOPI_CONFIGURATION_CRONTAB_MONTH_OF_YEAR",
        environ_prefix=None,
    )

    # Malware detection
    MALWARE_DETECTION = {
        "BACKEND": values.Value(
            "lasuite.malware_detection.backends.dummy.DummyBackend",
            environ_name="MALWARE_DETECTION_BACKEND",
            environ_prefix=None,
        ),
        "PARAMETERS": values.DictValue(
            default={
                "callback_path": "core.malware_detection.malware_detection_callback",
            },
            environ_name="MALWARE_DETECTION_PARAMETERS",
            environ_prefix=None,
        ),
    }

    # Metrics
    METRICS_ENABLED = values.BooleanValue(
        False,
        environ_name="METRICS_ENABLED",
        environ_prefix=None,
    )

    METRICS_USER_CLAIMS_EXPOSED = values.ListValue(
        [],
        environ_name="METRICS_USER_CLAIMS_EXPOSED",
        environ_prefix=None,
    )

    # Storage compute
    STORAGE_COMPUTE_BACKEND = values.Value(
        "core.storage.creator_storage_compute_backend.CreatorStorageComputeBackend",
        environ_name="STORAGE_COMPUTE_BACKEND",
        environ_prefix=None,
    )

    # Entitlements
    ENTITLEMENTS_BACKEND = values.Value(
        "core.entitlements.backends.static.StaticEntitlementsBackend",
        environ_name="ENTITLEMENTS_BACKEND",
        environ_prefix=None,
    )

    ENTITLEMENTS_BACKEND_PARAMETERS = values.DictValue(
        {},
        environ_name="ENTITLEMENTS_BACKEND_PARAMETERS",
        environ_prefix=None,
    )

    # pylint: disable=invalid-name
    @property
    def ENVIRONMENT(self):
        """Environment in which the application is launched."""
        return self.__class__.__name__.lower()

    # pylint: disable=invalid-name
    @property
    def RELEASE(self):
        """
        Return the release information.

        Delegate to the module function to enable easier testing.
        """
        return get_release()

    # pylint: disable=invalid-name
    @property
    def PARLER_LANGUAGES(self):
        """
        Return languages for Parler computed from the LANGUAGES and LANGUAGE_CODE settings.
        """
        return {
            self.SITE_ID: tuple({"code": code} for code, _name in self.LANGUAGES),
            "default": {
                "fallbacks": [self.LANGUAGE_CODE],
                "hide_untranslated": False,
            },
        }

    @classmethod
    def post_setup(cls):
        """Post setup configuration.
        This is the place where you can configure settings that require other
        settings to be loaded.
        """
        super().post_setup()

        # The SENTRY_DSN setting should be available to activate sentry for an environment
        if cls.SENTRY_DSN is not None:
            sentry_sdk.init(
                dsn=cls.SENTRY_DSN,
                environment=cls.__name__.lower(),
                release=get_release(),
                integrations=[DjangoIntegration()],
            )
            sentry_sdk.set_tag("application", "backend")

        if cls.OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION and cls.OIDC_ALLOW_DUPLICATE_EMAILS:
            raise ValueError(
                "Both OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION and "
                "OIDC_ALLOW_DUPLICATE_EMAILS cannot be set to True simultaneously. "
            )

        if cls.POSTHOG_KEY is not None:
            posthog.api_key = cls.POSTHOG_KEY
            posthog.host = cls.POSTHOG_HOST

        if cls.WOPI_CLIENTS:
            for wopi_client in cls.WOPI_CLIENTS:
                wopi_client_upper = wopi_client.upper()

                cls.WOPI_CLIENTS_CONFIGURATION[wopi_client] = {
                    "discovery_url": values.Value(
                        None,
                        environ_name=f"WOPI_{wopi_client_upper}_DISCOVERY_URL",
                        environ_prefix=None,
                        environ_required=True,
                    ),
                    "mimetypes": {},
                    "extensions": {},
                    "options": values.DictValue(
                        {},
                        environ_name=f"WOPI_{wopi_client_upper}_OPTIONS",
                        environ_prefix=None,
                    ),
                }


class Build(Base):
    """Settings used when the application is built.

    This environment should not be used to run the application. Just to build it with non-blocking
    settings.
    """

    SESSION_CACHE_ALIAS = "default"
    CACHES = {
        "default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"},
    }
    SECRET_KEY = values.Value("DummyKey")
    STORAGES = {
        "default": {
            "BACKEND": "django.core.files.storage.FileSystemStorage",
        },
        "staticfiles": {
            "BACKEND": values.Value(
                "whitenoise.storage.CompressedManifestStaticFilesStorage",
                environ_name="STORAGES_STATICFILES_BACKEND",
            ),
        },
    }


class Development(Base):
    """
    Development environment settings

    We set DEBUG to True and configure the server to respond from all hosts.
    """

    ALLOWED_HOSTS = ["*"]
    CORS_ALLOW_ALL_ORIGINS = True
    CSRF_TRUSTED_ORIGINS = [
        "http://localhost:8072",
        "http://localhost:3000",
        *values.ListValue([], environ_name="CSRF_TRUSTED_ORIGINS"),
    ]
    DEBUG = True
    LOAD_E2E_URLS = True

    SESSION_COOKIE_NAME = "drive_sessionid"

    USE_SWAGGER = True

    DEBUG_TOOLBAR_CONFIG = {
        "SHOW_TOOLBAR_CALLBACK": lambda request: True,
    }

    def __init__(self):
        # pylint: disable=invalid-name
        self.MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
        # pylint: disable=invalid-name
        self.INSTALLED_APPS += [
            "django_extensions",
            "drf_spectacular_sidecar",
            "debug_toolbar",
            "e2e",
            "demo",
        ]


class Test(Base):
    """Test environment settings"""

    SESSION_CACHE_ALIAS = "default"
    CACHES = {
        "default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"},
    }

    PASSWORD_HASHERS = [
        "django.contrib.auth.hashers.MD5PasswordHasher",
    ]
    USE_SWAGGER = True

    CELERY_TASK_ALWAYS_EAGER = values.BooleanValue(True)

    FEATURES_INDEXED_SEARCH = True

    SEARCH_INDEXER_CLASS = None
    OIDC_STORE_ACCESS_TOKEN = False
    OIDC_STORE_REFRESH_TOKEN = False

    ENTITLEMENTS_BACKEND = "core.entitlements.backends.static.StaticEntitlementsBackend"
    ENTITLEMENTS_BACKEND_PARAMETERS = {}

    def __init__(self):
        # pylint: disable=invalid-name
        self.INSTALLED_APPS += ["drf_spectacular_sidecar", "e2e", "demo"]


class ContinuousIntegration(Test):
    """
    Continuous Integration environment settings

    nota bene: it should inherit from the Test environment.
    """


class Production(Base):
    """
    Production environment settings

    You must define the ALLOWED_HOSTS environment variable in Production
    configuration (and derived configurations):
    ALLOWED_HOSTS=["foo.com", "foo.fr"]
    """

    # Security
    # Add allowed host from environment variables.
    # The machine hostname is added by default,
    # it makes the application pingable by a load balancer on the same machine by example
    ALLOWED_HOSTS = [
        *values.ListValue([], environ_name="ALLOWED_HOSTS"),
        gethostbyname(gethostname()),
    ]
    CSRF_TRUSTED_ORIGINS = values.ListValue([])
    SECURE_BROWSER_XSS_FILTER = True
    SECURE_CONTENT_TYPE_NOSNIFF = True

    # SECURE_PROXY_SSL_HEADER allows to fix the scheme in Django's HttpRequest
    # object when your application is behind a reverse proxy.
    #
    # Keep this SECURE_PROXY_SSL_HEADER configuration only if :
    # - your Django app is behind a proxy.
    # - your proxy strips the X-Forwarded-Proto header from all incoming requests
    # - Your proxy sets the X-Forwarded-Proto header and sends it to Django
    #
    # In other cases, you should comment the following line to avoid security issues.
    # SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
    SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
    SECURE_HSTS_SECONDS = 60
    SECURE_HSTS_PRELOAD = True
    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
    SECURE_SSL_REDIRECT = True
    SECURE_REDIRECT_EXEMPT = [
        "^__lbheartbeat__",
        "^__heartbeat__",
    ]

    # Modern browsers require to have the `secure` attribute on cookies with `Samesite=none`
    CSRF_COOKIE_SECURE = True
    SESSION_COOKIE_SECURE = True

    # Privacy
    SECURE_REFERRER_POLICY = "same-origin"


class Feature(Production):
    """
    Feature environment settings

    nota bene: it should inherit from the Production environment.
    """


class Staging(Production):
    """
    Staging environment settings

    nota bene: it should inherit from the Production environment.
    """


class PreProduction(Production):
    """
    Pre-production environment settings

    nota bene: it should inherit from the Production environment.
    """
