"""
Django settings for Meet 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/
"""

# pylint: disable=too-many-lines

import json
import warnings
from os import path
from socket import gethostbyname, gethostname

from django.utils.translation import gettext_lazy as _

import dj_database_url
import sentry_sdk
from configurations import Configuration, values
from lasuite.configuration.values import SecretFileValue
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import ignore_logger

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = path.dirname(path.dirname(path.abspath(__file__)))

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


def get_release():
    """
    Get the current release of the application

    By release, we mean the release from the version.json file à la Mozilla [1]
    (if any). If this file has not been found, it defaults to "NA".

    [1]
    https://github.com/mozilla-services/Dockerflow/blob/master/docs/version_object.md
    """
    # Try to get the current release from the version.json file generated by the
    # CI during the Docker image build
    try:
        with open(path.join(BASE_DIR, "version.json"), encoding="utf8") as version:
            return json.load(version)["version"]
    except FileNotFoundError:
        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:

    * DJANGO_SENTRY_DSN
    * DB_NAME
    * DB_HOST
    * DB_PASSWORD
    * DB_USER
    """

    DEBUG = False
    USE_SWAGGER = False

    API_VERSION = "v1.0"
    EXTERNAL_API_VERSION = "v1.0"
    EXTERNAL_API_ENABLED = values.BooleanValue(
        False, environ_name="EXTERNAL_API_ENABLED", environ_prefix=None
    )

    DATA_DIR = values.Value(path.join("/", "data"), environ_name="DATA_DIR")

    # Security
    ALLOWED_HOSTS = values.ListValue([])
    SECRET_KEY = SecretFileValue(None)
    SILENCED_SYSTEM_CHECKS = values.ListValue([])
    ALLOW_UNSECURE_USER_LISTING = values.BooleanValue(
        False, environ_name="ALLOW_UNSECURE_USER_LISTING", environ_prefix=None
    )

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

    # Database
    DATABASES = {
        "default": dj_database_url.config()
        if values.DatabaseURLValue(
            None, environ_name="DATABASE_URL", environ_prefix=None
        )
        else {
            "ENGINE": values.Value(
                "django.db.backends.postgresql_psycopg2",
                environ_name="DB_ENGINE",
                environ_prefix=None,
            ),
            "NAME": values.Value("meet", 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"

    # Static files (CSS, JavaScript, Images)
    STATIC_URL = "/static/"
    STATIC_ROOT = path.join(DATA_DIR, "static")
    MEDIA_URL = "/media/"
    MEDIA_ROOT = path.join(DATA_DIR, "media")
    MEDIA_BASE_URL = values.Value(
        "", environ_name="MEDIA_BASE_URL", environ_prefix=None
    )
    # Header the reverse proxy uses to pass the original request URL to the
    # media-auth subrequest views. nginx-ingress sends X-Original-URL, which is
    # the default. Other proxies use different headers -- Traefik's ForwardAuth,
    # for instance, sends X-Forwarded-Uri and cannot emit X-Original-URL at all.
    MEDIA_AUTH_ORIGINAL_URL_HEADER = values.Value(
        default="HTTP_X_ORIGINAL_URL",
        environ_name="MEDIA_AUTH_ORIGINAL_URL_HEADER",
        environ_prefix=None,
    )

    SITE_ID = 1

    STORAGES = {
        "default": {
            "BACKEND": "storages.backends.s3.S3Storage",
        },
        "staticfiles": {
            "BACKEND": values.Value(
                "whitenoise.storage.CompressedManifestStaticFilesStorage",
                environ_name="STORAGES_STATICFILES_BACKEND",
            ),
        },
    }

    # 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(
        "meet-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,
    )

    FILE_UPLOAD_ENABLED = values.BooleanValue(
        # False to avoid a breaking change for now
        default=False,
        environ_name="FILE_UPLOAD_ENABLED",
        environ_prefix=None,
    )
    FILE_PURGE_GRACE_DAYS = values.PositiveIntegerValue(
        default=7, environ_name="FILE_PURGE_GRACE_DAYS", environ_prefix=None
    )
    FILE_UPLOAD_PATH = values.Value(
        "files", environ_name="FILE_UPLOAD_PATH", environ_prefix=None
    )
    FILE_UPLOAD_TMP_PATH = values.Value(
        "tmp/files", environ_name="FILE_UPLOAD_TMP_PATH", environ_prefix=None
    )

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

    FILE_UPLOAD_RESTRICTIONS = values.DictValue(
        {
            "background_image": {
                "max_size": 2 * MB,
                "max_count_by_user": 10,
                "allowed_extensions": [".jpeg", ".jpg", ".png"],
                "allowed_mimetypes": ["image/jpeg", "image/png"],
            },
        },
        environ_name="FILE_UPLOAD_RESTRICTIONS",
        environ_prefix=None,
    )

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

    # Languages
    LANGUAGE_CODE = values.Value("en-us")

    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")),
            ("nl-nl", _("Dutch")),
            ("de-de", _("German")),
            ("es-es", _("Spanish")),
        )
    )

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

    TIME_ZONE = "UTC"
    USE_I18N = True
    USE_TZ = True

    # Templates
    TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "DIRS": [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 = [
        # Meet
        "core",
        "demo",
        "drf_spectacular",
        # Third party apps
        "corsheaders",
        "dockerflow.django",
        "rest_framework",
        "parler",
        "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",
    ]

    # Cache
    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": values.Value(
                "redis://redis:6379/1",
                environ_name="REDIS_URL",
                environ_prefix=None,
            ),
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
        },
    }

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            "core.authentication.backends.SessionAuthenticationWith401",
        ),
        "DEFAULT_PARSER_CLASSES": [
            "rest_framework.parsers.JSONParser",
            "nested_multipart_parser.drf.DrfNestedParser",
        ],
        "DEFAULT_RENDERER_CLASSES": [
            "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_RATES": {
            "request_entry": values.Value(
                default="150/minute",
                environ_name="REQUEST_ENTRY_THROTTLE_RATES",
                environ_prefix=None,
            ),
            "creation_callback": values.Value(
                default="600/minute",
                environ_name="CREATION_CALLBACK_THROTTLE_RATES",
                environ_prefix=None,
            ),
            "roomkit_join": values.Value(
                default="300/minute",
                environ_name="ROOMKIT_JOIN_THROTTLE_RATES",
                environ_prefix=None,
            ),
            "connection_test": values.Value(
                default="30/minute",
                environ_name="CONNECTION_TEST_THROTTLE_RATES",
                environ_prefix=None,
            ),
        },
    }
    MONITORED_THROTTLE_FAILURE_CALLBACK = (
        "core.api.throttling.sentry_monitoring_throttle_failure"
    )

    SPECTACULAR_SETTINGS = {
        "TITLE": "Meet API",
        "DESCRIPTION": "This is the Meet 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",
    }

    # Frontend
    FRONTEND_CONFIGURATION = {
        # If set, a <link> tag with this URL as href is added to the <head> of the frontend app.
        # This is useful if you want to change CSS variables to customize the look of the app.
        "custom_css_url": values.Value(
            None, environ_name="FRONTEND_CUSTOM_CSS_URL", environ_prefix=None
        ),
        "analytics": values.DictValue(
            {}, environ_name="FRONTEND_ANALYTICS", environ_prefix=None
        ),
        "support": values.DictValue(
            {}, environ_name="FRONTEND_SUPPORT", environ_prefix=None
        ),
        "silence_livekit_debug_logs": values.BooleanValue(
            False, environ_name="FRONTEND_SILENCE_LIVEKIT_DEBUG", environ_prefix=None
        ),
        "is_silent_login_enabled": values.BooleanValue(
            True, environ_name="FRONTEND_IS_SILENT_LOGIN_ENABLED", environ_prefix=None
        ),
        "idle_disconnect_warning_delay": values.PositiveIntegerValue(
            None,
            environ_name="FRONTEND_IDLE_DISCONNECT_WARNING_DELAY",
            environ_prefix=None,
        ),
        "feedback": values.DictValue(
            {}, environ_name="FRONTEND_FEEDBACK", environ_prefix=None
        ),
        "documentation_url": values.Value(
            None, environ_name="FRONTEND_DOCUMENTATION_URL", environ_prefix=None
        ),
        "external_home_url": values.Value(
            None, environ_name="FRONTEND_EXTERNAL_HOME_URL", environ_prefix=None
        ),
        "use_french_gov_footer": values.BooleanValue(
            False, environ_name="FRONTEND_USE_FRENCH_GOV_FOOTER", environ_prefix=None
        ),
        "use_proconnect_button": values.BooleanValue(
            False, environ_name="FRONTEND_USE_PROCONNECT_BUTTON", environ_prefix=None
        ),
        "manifest_link": values.Value(
            None, environ_name="FRONTEND_MANIFEST_LINK", environ_prefix=None
        ),
        "transcription_destination": values.Value(
            None, environ_name="FRONTEND_TRANSCRIPTION_DESTINATION", environ_prefix=None
        ),
        "max_participants_for_sound": values.PositiveIntegerValue(
            5, environ_name="FRONTEND_MAX_PARTICIPANTS_FOR_SOUND", environ_prefix=None
        ),
        "auto_mute_on_join_threshold": values.PositiveIntegerValue(
            50, environ_name="FRONTEND_AUTO_MUTE_ON_JOIN_THRESHOLD", environ_prefix=None
        ),
    }

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

    AUTH_USER_MODEL = "core.User"

    # 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")

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

    # Celery
    # Defaults to False to avoid breaking change, async task will be run
    # synchronously
    CELERY_ENABLED = values.BooleanValue(False, environ_prefix=None)
    CELERY_TASK_ALWAYS_EAGER = values.BooleanValue(False, environ_prefix=None)
    CELERY_TASK_DEFAULT_QUEUE = values.Value("meet-backend", environ_prefix=None)
    CELERY_BROKER_URL = values.Value("redis://redis:6379/0", environ_prefix=None)
    CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({}, environ_prefix=None)

    # Session
    SESSION_ENGINE = values.Value(
        default="django.contrib.sessions.backends.cache",
        environ_name="SESSION_ENGINE",
        environ_prefix=None,
    )
    SESSION_CACHE_ALIAS = "default"
    SESSION_COOKIE_AGE = values.PositiveIntegerValue(
        default=60 * 60 * 12, environ_name="SESSION_COOKIE_AGE", environ_prefix=None
    )

    # OIDC - Authorization Code Flow
    OIDC_AUTHENTICATE_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationRequestView"
    OIDC_CALLBACK_CLASS = "lasuite.oidc_login.views.OIDCAuthenticationCallbackView"
    OIDC_CREATE_USER = values.BooleanValue(
        default=True, environ_name="OIDC_CREATE_USER", environ_prefix=None
    )
    OIDC_VERIFY_SSL = values.BooleanValue(
        default=True, environ_name="OIDC_VERIFY_SSL", environ_prefix=None
    )
    OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = values.BooleanValue(
        default=False,
        environ_name="OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION",
        environ_prefix=None,
    )
    OIDC_USER_SUB_FIELD_IMMUTABLE = values.BooleanValue(
        default=True, environ_name="OIDC_USER_SUB_FIELD_IMMUTABLE", environ_prefix=None
    )
    OIDC_TIMEOUT = values.IntegerValue(
        5, environ_name="OIDC_TIMEOUT", environ_prefix=None
    )
    OIDC_PROXY = values.Value(None, environ_name="OIDC_PROXY", environ_prefix=None)
    OIDC_RP_SIGN_ALGO = values.Value(
        "RS256", environ_name="OIDC_RP_SIGN_ALGO", environ_prefix=None
    )
    OIDC_RP_CLIENT_ID = values.Value(
        "meet", 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_INTROSPECTION_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_INTROSPECTION_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_USER_ENDPOINT_FORMAT = values.Value(
        "AUTO", environ_name="OIDC_OP_USER_ENDPOINT_FORMAT", environ_prefix=None
    )
    OIDC_OP_LOGOUT_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_LOGOUT_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_URL = values.Value(None, environ_name="OIDC_OP_URL", 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
    )
    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
    )
    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
    )
    ALLOW_LOGOUT_GET_METHOD = values.BooleanValue(
        default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None
    )
    OIDC_REDIRECT_FIELD_NAME = values.Value(
        "returnTo", environ_name="OIDC_REDIRECT_FIELD_NAME", environ_prefix=None
    )
    OIDC_USERINFO_FULLNAME_FIELDS = values.ListValue(
        default=["given_name", "usual_name"],
        environ_name="OIDC_USERINFO_FULLNAME_FIELDS",
        environ_prefix=None,
    )
    OIDC_USERINFO_SHORTNAME_FIELD = values.Value(
        default="given_name",
        environ_name="OIDC_USERINFO_SHORTNAME_FIELD",
        environ_prefix=None,
    )
    OIDC_USERINFO_ESSENTIAL_CLAIMS = values.ListValue(
        default=[],
        environ_name="OIDC_USERINFO_ESSENTIAL_CLAIMS",
        environ_prefix=None,
    )

    # OIDC Resource Server Backend
    OIDC_RS_BACKEND_CLASS = "core.external_api.authentication.ResourceServerBackend"
    OIDC_RS_CLIENT_ID = values.Value(
        "meet", environ_name="OIDC_RS_CLIENT_ID", environ_prefix=None
    )
    OIDC_RS_CLIENT_SECRET = SecretFileValue(
        None,
        environ_name="OIDC_RS_CLIENT_SECRET",
        environ_prefix=None,
    )
    OIDC_RS_AUDIENCE_CLAIM = values.Value(
        default="client_id", environ_name="OIDC_RS_AUDIENCE_CLAIM", environ_prefix=None
    )
    OIDC_RS_ENCRYPTION_ENCODING = values.Value(
        default="A256GCM",
        environ_name="OIDC_RS_ENCRYPTION_ENCODING",
        environ_prefix=None,
    )
    OIDC_RS_ENCRYPTION_ALGO = values.Value(
        default="RSA-OAEP", environ_name="OIDC_RS_ENCRYPTION_ALGO", environ_prefix=None
    )
    OIDC_RS_SIGNING_ALGO = values.Value(
        default="ES256", environ_name="OIDC_RS_SIGNING_ALGO", environ_prefix=None
    )
    OIDC_RS_SCOPES = values.ListValue(
        default=["lasuite_meet"],
        environ_name="OIDC_RS_SCOPES",
        environ_prefix=None,
    )
    OIDC_RS_PRIVATE_KEY_STR = SecretFileValue(
        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
    )
    OIDC_RS_SCOPES_PREFIX = values.Value(
        default=None, environ_name="OIDC_RS_SCOPES_PREFIX", environ_prefix=None
    )

    # Video conference configuration
    LIVEKIT_CONFIGURATION = {
        "api_key": SecretFileValue(environ_name="LIVEKIT_API_KEY", environ_prefix=None),
        "api_secret": SecretFileValue(
            environ_name="LIVEKIT_API_SECRET", environ_prefix=None
        ),
        "url": values.Value(environ_name="LIVEKIT_API_URL", environ_prefix=None),
    }
    LIVEKIT_FORCE_WSS_PROTOCOL = values.BooleanValue(
        False, environ_name="LIVEKIT_FORCE_WSS_PROTOCOL", environ_prefix=None
    )
    LIVEKIT_DEFAULT_SOURCES = values.ListValue(
        [
            "camera",
            "microphone",
            "screen_share",
            "screen_share_audio",
        ],
        environ_name="LIVEKIT_DEFAULT_SOURCES",
        environ_prefix=None,
    )
    LIVEKIT_ENABLE_FIREFOX_PROXY_WORKAROUND = values.BooleanValue(
        environ_name="LIVEKIT_ENABLE_FIREFOX_PROXY_WORKAROUND",
        environ_prefix=None,
        default=False,
    )
    CONNECTION_TEST_ENABLED = values.BooleanValue(
        environ_name="CONNECTION_TEST_ENABLED",
        environ_prefix=None,
        default=False,
    )
    CONNECTION_TEST_TOKEN_TTL_SECONDS = values.PositiveIntegerValue(
        300,
        environ_name="CONNECTION_TEST_TOKEN_TTL_SECONDS",
        environ_prefix=None,
    )
    # The effective room max age is always computed as
    # CONNECTION_TEST_TOKEN_TTL_SECONDS + this value. Token expiration does
    # not automatically delete rooms, so once that age is reached, the
    # cleanup worker will explicitly delete the room if it still exists.
    CONNECTION_TEST_ROOM_EXTRA_AGE_SECONDS = values.PositiveIntegerValue(
        10,
        environ_name="CONNECTION_TEST_ROOM_EXTRA_AGE_SECONDS",
        environ_prefix=None,
    )
    CONNECTION_TEST_ROOM_PREFIX = values.Value(
        "connection-test",
        environ_name="CONNECTION_TEST_ROOM_PREFIX",
        environ_prefix=None,
    )
    LIVEKIT_VERIFY_SSL = values.BooleanValue(
        True, environ_name="LIVEKIT_VERIFY_SSL", environ_prefix=None
    )
    # Regex to filter webhook events by room name. Only matching events are processed.
    LIVEKIT_WEBHOOK_EVENTS_FILTER_REGEX = values.Value(
        None, environ_name="LIVEKIT_WEBHOOK_EVENTS_FILTER_REGEX", environ_prefix=None
    )
    RESOURCE_DEFAULT_ACCESS_LEVEL = values.Value(
        "public", environ_name="RESOURCE_DEFAULT_ACCESS_LEVEL", environ_prefix=None
    )
    ALLOW_UNREGISTERED_ROOMS = values.BooleanValue(
        True, environ_name="ALLOW_UNREGISTERED_ROOMS", environ_prefix=None
    )
    # if provided, treat as suspicious (possible privilege escalation attempt).
    PARTICIPANT_FORBIDDEN_PERMISSION_FIELDS = values.ListValue(
        ["hidden", "recorder", "agent"],
        environ_name="PARTICIPANT_FORBIDDEN_PERMISSION_FIELDS",
        environ_prefix=None,
    )
    AUTHENTICATED_PARTICIPANTS_CAN_EDIT_DISPLAY_NAME = values.BooleanValue(
        True,
        environ_name="AUTHENTICATED_PARTICIPANTS_CAN_EDIT_DISPLAY_NAME",
        environ_prefix=None,
    )

    # Recording settings
    RECORDING_ENABLE = values.BooleanValue(
        False, environ_name="RECORDING_ENABLE", environ_prefix=None
    )
    RECORDING_OUTPUT_FOLDER = values.Value(
        "recordings", environ_name="RECORDING_OUTPUT_FOLDER", environ_prefix=None
    )
    RECORDING_WORKER_CLASSES = values.DictValue(
        {
            "screen_recording": "core.recording.worker.services.VideoCompositeEgressService",
            "transcript": "core.recording.worker.services.AudioCompositeEgressService",
        },
        environ_name="RECORDING_WORKER_CLASSES",
        environ_prefix=None,
    )
    # Number of days before recordings expire - must be synced with bucket lifecycle policy
    # Set to None for no expiration
    RECORDING_EXPIRATION_DAYS = values.IntegerValue(
        None, environ_name="RECORDING_EXPIRATION_DAYS", environ_prefix=None
    )
    # Recording max duration in milliseconds - must be synced with LiveKit Egress configuration
    # Set to None for no max duration
    RECORDING_MAX_DURATION = values.IntegerValue(
        None, environ_name="RECORDING_MAX_DURATION", environ_prefix=None
    )

    # Recording encoding options for LiveKit Egress (video composite egress only).
    # These settings affect screen recordings handled by VideoCompositeEgressService;
    # they are silently ignored by AudioCompositeEgressService (audio-only transcript
    # recordings), whose request never carries advanced EncodingOptions.
    # When disabled, LiveKit falls back to its built-in H264_720P_30 preset
    # (1280x720, 30 fps, 3000 kbps H.264 MAIN video, 128 kbps AAC audio).
    # When enabled, the values below are passed to LiveKit as EncodingOptions
    # (advanced) and replace the preset. Lowering framerate and bitrate reduces
    # output file size and CPU load on the egress worker.
    RECORDING_ENCODING_ENABLED = values.BooleanValue(
        False, environ_name="RECORDING_ENCODING_ENABLED", environ_prefix=None
    )
    RECORDING_ENCODING_WIDTH = values.PositiveIntegerValue(
        1280, environ_name="RECORDING_ENCODING_WIDTH", environ_prefix=None
    )
    RECORDING_ENCODING_HEIGHT = values.PositiveIntegerValue(
        720, environ_name="RECORDING_ENCODING_HEIGHT", environ_prefix=None
    )
    RECORDING_ENCODING_FRAMERATE = values.PositiveIntegerValue(
        30, environ_name="RECORDING_ENCODING_FRAMERATE", environ_prefix=None
    )
    RECORDING_ENCODING_VIDEO_BITRATE_KBPS = values.PositiveIntegerValue(
        3000,
        environ_name="RECORDING_ENCODING_VIDEO_BITRATE_KBPS",
        environ_prefix=None,
    )
    RECORDING_ENCODING_AUDIO_BITRATE_KBPS = values.PositiveIntegerValue(
        128,
        environ_name="RECORDING_ENCODING_AUDIO_BITRATE_KBPS",
        environ_prefix=None,
    )
    RECORDING_ENCODING_KEY_FRAME_INTERVAL_S = values.FloatValue(
        4.0,
        environ_name="RECORDING_ENCODING_KEY_FRAME_INTERVAL_S",
        environ_prefix=None,
    )

    SUMMARY_SERVICE_VERSION = values.PositiveIntegerValue(
        1, environ_name="SUMMARY_SERVICE_VERSION", environ_prefix=None
    )
    SUMMARY_SERVICE_ENDPOINT = values.Value(
        None, environ_name="SUMMARY_SERVICE_ENDPOINT", environ_prefix=None
    )
    SUMMARY_SERVICE_API_TOKEN = SecretFileValue(
        None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None
    )
    SUMMARY_SERVICE_WEBHOOK_API_TOKEN = SecretFileValue(
        None, environ_name="SUMMARY_SERVICE_WEBHOOK_API_TOKEN", environ_prefix=None
    )
    SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS = (
        values.PositiveIntegerValue(
            60 * 60 * 24,
            environ_name="SUMMARY_SERVICE_CLOUD_STORAGE_SIGNED_URL_EXPIRY_SECONDS",
            environ_prefix=None,
        )
    )
    TRANSCRIPTION_SATISFACTION_FORM_BASE_URL = values.Value(
        None,
        environ_name="TRANSCRIPTION_SATISFACTION_FORM_BASE_URL",
        environ_prefix=None,
    )
    SCREEN_RECORDING_BASE_URL = values.Value(
        None, environ_name="SCREEN_RECORDING_BASE_URL", environ_prefix=None
    )
    RECORDING_DOWNLOAD_BASE_URL = values.Value(
        None, environ_name="RECORDING_DOWNLOAD_BASE_URL", environ_prefix=None
    )

    # Analytics
    ANALYTICS_BACKEND = values.Value(
        None, environ_name="ANALYTICS_BACKEND", environ_prefix=None
    )
    ANALYTICS_BACKEND_SETTINGS = values.DictValue(
        {}, environ_name="ANALYTICS_BACKEND_SETTINGS", environ_prefix=None
    )

    # Marketing and communication settings
    SIGNUP_NEW_USER_TO_MARKETING_EMAIL = values.BooleanValue(
        False,  # When enabled, new users are automatically added to mailing list.
        environ_name="SIGNUP_NEW_USER_TO_MARKETING_EMAIL",
        environ_prefix=None,
    )
    MARKETING_SERVICE_CLASS = values.Value(
        "core.services.marketing.BrevoMarketingService",
        environ_name="MARKETING_SERVICE_CLASS",
        environ_prefix=None,
    )
    BREVO_API_KEY = SecretFileValue(
        None, environ_name="BREVO_API_KEY", environ_prefix=None
    )
    BREVO_API_CONTACT_LIST_IDS = values.ListValue(
        [],
        environ_name="BREVO_API_CONTACT_LIST_IDS",
        environ_prefix=None,
        converter=int,
    )
    BREVO_API_CONTACT_ATTRIBUTES = values.DictValue({"VISIO_USER": True})
    BREVO_API_TIMEOUT = values.PositiveIntegerValue(
        1, environ_name="BREVO_API_TIMEOUT", environ_prefix=None
    )

    # Lobby configurations
    PRESENCE_KEY_PREFIX = values.Value(
        "room_presence", environ_name="PRESENCE_KEY_PREFIX", environ_prefix=None
    )
    PRESENCE_CACHE_TIMEOUT = values.PositiveIntegerValue(
        3600, environ_name="PRESENCE_CACHE_TIMEOUT", environ_prefix=None
    )
    PRESENCE_CLEAR_ON_PARTICIPANT_LEFT = values.BooleanValue(
        True, environ_name="PRESENCE_CLEAR_ON_PARTICIPANT_LEFT", environ_prefix=None
    )
    LOBBY_KEY_PREFIX = values.Value(
        "room_lobby", environ_name="LOBBY_KEY_PREFIX", environ_prefix=None
    )
    LOBBY_WAITING_TIMEOUT = values.PositiveIntegerValue(
        3, environ_name="LOBBY_WAITING_TIMEOUT", environ_prefix=None
    )
    LOBBY_DENIED_TIMEOUT = values.PositiveIntegerValue(
        5, environ_name="LOBBY_DENIED_TIMEOUT", environ_prefix=None
    )
    LOBBY_ACCEPTED_TIMEOUT = values.PositiveIntegerValue(
        21600,  # 6hrs
        environ_name="LOBBY_ACCEPTED_TIMEOUT",
        environ_prefix=None,
    )
    LOBBY_NOTIFICATION_TYPE = values.Value(
        "participantWaiting",
        environ_name="LOBBY_NOTIFICATION_TYPE",
        environ_prefix=None,
    )
    LOBBY_COOKIE_NAME = values.Value(
        "lobbyParticipantId",
        environ_name="LOBBY_COOKIE_NAME",
        environ_prefix=None,
    )

    # Calendar integrations
    ROOM_CREATION_CALLBACK_CACHE_TIMEOUT = values.PositiveIntegerValue(
        600,  # 10 minutes
        environ_name="ROOM_CREATION_CALLBACK_CACHE_TIMEOUT",
        environ_prefix=None,
    )

    # SIP Telephony
    ROOM_TELEPHONY_ENABLED = values.BooleanValue(
        False,
        environ_name="ROOM_TELEPHONY_ENABLED",
        environ_prefix=None,
    )
    ROOM_TELEPHONY_PIN_LENGTH = values.PositiveIntegerValue(
        10,
        environ_name="ROOM_TELEPHONY_PIN_LENGTH",
        environ_prefix=None,
    )
    ROOM_TELEPHONY_PIN_MAX_RETRIES = values.PositiveIntegerValue(
        5,
        environ_name="ROOM_TELEPHONY_PIN_MAX_RETRIES",
        environ_prefix=None,
    )
    ROOM_TELEPHONY_PHONE_NUMBER = values.Value(
        None,
        environ_name="ROOM_TELEPHONY_PHONE_NUMBER",
        environ_prefix=None,
    )
    ROOM_TELEPHONY_DEFAULT_COUNTRY = values.Value(
        "US",
        environ_name="ROOM_TELEPHONY_DEFAULT_COUNTRY",
        environ_prefix=None,
    )

    # Roomkit (meeting-room SIP devices) integration
    ROOMKIT_ENABLED = values.BooleanValue(
        False,
        environ_name="ROOMKIT_ENABLED",
        environ_prefix=None,
    )
    # Server-to-server API token allowing the LiveKit SIP module to call the
    # roomkit endpoints (e.g. join a room on behalf of a meeting-room device
    # dialing in before any WebRTC participant).
    ROOMKIT_SERVER_TO_SERVER_API_TOKEN = SecretFileValue(
        None,
        environ_name="ROOMKIT_SERVER_TO_SERVER_API_TOKEN",
        environ_prefix=None,
    )

    # Subtitles settings
    ROOM_SUBTITLE_ENABLED = values.BooleanValue(
        False, environ_name="ROOM_SUBTITLE_ENABLED", environ_prefix=None
    )
    ROOM_SUBTITLE_AGENT_NAME = values.Value(
        "multi-user-transcriber",
        environ_name="ROOM_SUBTITLE_AGENT_NAME",
        environ_prefix=None,
    )

    # Metadata collector settings
    METADATA_COLLECTOR_ENABLED = values.BooleanValue(
        False, environ_name="METADATA_COLLECTOR_ENABLED", environ_prefix=None
    )
    METADATA_COLLECTOR_AGENT_NAME = values.Value(
        "metadata-collector",
        environ_name="METADATA_COLLECTOR_AGENT_NAME",
        environ_prefix=None,
    )
    METADATA_COLLECTOR_OUTPUT_FOLDER = values.Value(
        "metadata",
        environ_name="METADATA_COLLECTOR_OUTPUT_FOLDER",
        environ_prefix=None,
    )

    # External Applications
    APPLICATION_ENABLED = values.BooleanValue(
        False, environ_name="APPLICATION_ENABLED", environ_prefix=None
    )
    APPLICATION_CLIENT_ID_LENGTH = values.PositiveIntegerValue(
        40,
        environ_name="APPLICATION_CLIENT_ID_LENGTH",
        environ_prefix=None,
    )
    APPLICATION_CLIENT_SECRET_LENGTH = values.PositiveIntegerValue(
        128,
        environ_name="APPLICATION_CLIENT_SECRET_LENGTH",
        environ_prefix=None,
    )
    APPLICATION_JWT_SECRET_KEY = SecretFileValue(
        None, environ_name="APPLICATION_JWT_SECRET_KEY", environ_prefix=None
    )
    APPLICATION_JWT_ALG = values.Value(
        "HS256",
        environ_name="APPLICATION_JWT_ALG",
        environ_prefix=None,
    )
    APPLICATION_JWT_ISSUER = values.Value(
        "lasuite-meet",
        environ_name="APPLICATION_JWT_ISSUER",
        environ_prefix=None,
    )
    APPLICATION_JWT_AUDIENCE = values.Value(
        None,
        environ_name="APPLICATION_JWT_AUDIENCE",
        environ_prefix=None,
    )
    APPLICATION_JWT_EXPIRATION_SECONDS = values.PositiveIntegerValue(
        3600,
        environ_name="APPLICATION_JWT_EXPIRATION_SECONDS",
        environ_prefix=None,
    )
    APPLICATION_JWT_TOKEN_TYPE = values.Value(
        "Bearer",
        environ_name="APPLICATION_JWT_TOKEN_TYPE",
        environ_prefix=None,
    )
    APPLICATION_BASE_URL = values.Value(
        None,
        environ_name="APPLICATION_BASE_URL",
        environ_prefix=None,
    )
    # Warning: EXTERNAL_API_ALLOW_PUBLIC_ACCESS is ignored when
    # EXTERNAL_API_DEFAULT_ACCESS_LEVEL=public.
    EXTERNAL_API_ALLOW_PUBLIC_ACCESS = values.BooleanValue(
        False,
        environ_name="EXTERNAL_API_ALLOW_PUBLIC_ACCESS",
        environ_prefix=None,
    )
    EXTERNAL_API_DEFAULT_ACCESS_LEVEL = values.Value(
        "trusted",
        environ_name="EXTERNAL_API_DEFAULT_ACCESS_LEVEL",
        environ_prefix=None,
    )
    # Allows third-party platforms to create users with email-only identification.
    # Required for external integrations, but fragile due to deferred user reconciliation
    # on sub. Enable it with care /!\
    APPLICATION_ALLOW_USER_CREATION = values.BooleanValue(
        False,
        environ_name="APPLICATION_ALLOW_USER_CREATION",
        environ_prefix=None,
    )

    # Addons
    ADDONS_ENABLED = values.BooleanValue(
        False,
        environ_name="ADDONS_ENABLED",
        environ_prefix=None,
    )
    ADDONS_SESSION_TTL = values.PositiveIntegerValue(
        3600,
        environ_name="ADDONS_SESSION_TTL",
        environ_prefix=None,
    )
    ADDONS_TRANSIT_TOKEN_TTL = values.PositiveIntegerValue(
        120,
        environ_name="ADDONS_TRANSIT_TOKEN_TTL",
        environ_prefix=None,
    )
    ADDONS_CSRF_SECRET = SecretFileValue(
        None,
        environ_name="ADDONS_CSRF_SECRET",
        environ_prefix=None,
    )
    ADDONS_CACHE_PREFIX_SESSION = values.Value(
        "sid",
        environ_name="ADDONS_CACHE_PREFIX_SESSION",
        environ_prefix=None,
    )
    ADDONS_CACHE_PREFIX_TRANSIT = values.Value(
        "transit",
        environ_name="ADDONS_CACHE_PREFIX_TRANSIT",
        environ_prefix=None,
    )
    ADDONS_TOKEN_AUDIENCE = values.Value(
        "addons",
        environ_name="ADDONS_TOKEN_AUDIENCE",
        environ_prefix=None,
    )
    ADDONS_TOKEN_ISSUER = values.Value(
        "lasuite-meet",
        environ_name="ADDONS_TOKEN_ISSUER",
        environ_prefix=None,
    )
    ADDONS_TOKEN_TTL = values.PositiveIntegerValue(
        7200,
        environ_name="ADDONS_TOKEN_TTL",
        environ_prefix=None,
    )
    ADDONS_TOKEN_ALG = values.Value(
        "HS256",
        environ_name="ADDONS_TOKEN_ALG",
        environ_prefix=None,
    )
    ADDONS_TOKEN_TYPE = values.Value(
        "Bearer",
        environ_name="ADDONS_TOKEN_TYPE",
        environ_prefix=None,
    )
    ADDONS_TOKEN_SECRET_KEY = SecretFileValue(
        None,
        environ_name="ADDONS_TOKEN_SECRET_KEY",
        environ_prefix=None,
    )
    ADDONS_TOKEN_SCOPE = values.Value(
        "rooms:create",
        environ_name="ADDONS_TOKEN_SCOPE",
        environ_prefix=None,
    )
    ADDONS_RANDOM_TOKEN_BYTE_LENGTH = values.PositiveIntegerValue(
        60,
        environ_name="ADDONS_RANDOM_TOKEN_BYTE_LENGTH",
        environ_prefix=None,
    )
    ADDONS_SESSION_ID_COOKIE = values.Value(
        "addonsSid",
        environ_name="ADDONS_SESSION_ID_COOKIE",
        environ_prefix=None,
    )
    ADDONS_PENDING_SESSION_KEY = values.Value(
        "addons_sid",
        environ_name="ADDONS_PENDING_SESSION_KEY",
        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": False,
            },
        },
    }

    # 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()

        if cls.FILE_UPLOAD_TMP_PATH == cls.FILE_UPLOAD_PATH:
            raise ValueError(
                "FILE_UPLOAD_TMP_PATH cannot be the same as FILE_UPLOAD_PATH"
            )

        if (
            cls.SUMMARY_SERVICE_VERSION == 1
            and cls.SUMMARY_SERVICE_ENDPOINT is not None
        ):
            warnings.warn(
                "SUMMARY_SERVICE_VERSION=1 is deprecated. "
                "The legacy v1 API has been removed from the experimental "
                "Summary service. Please update your Summary service deployment to "
                "the v2 API and set SUMMARY_SERVICE_VERSION=2.",
                # We use UserWarning to make sure it shows up in production deployment
                UserWarning,
                stacklevel=2,
            )

        # 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(),  # build, test, development, production
                release=get_release(),
                integrations=[DjangoIntegration()],
            )
            sentry_sdk.set_tag("application", "backend")

            # Ignore the logs added by the DockerflowMiddleware
            ignore_logger("request.summary")


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.
    """

    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"]
    DEBUG = True

    SESSION_COOKIE_NAME = "meet_sessionid"

    USE_SWAGGER = True

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


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

    LOGGING = values.DictValue(
        {
            "version": 1,
            "disable_existing_loggers": False,
            "handlers": {
                "console": {
                    "class": "logging.StreamHandler",
                },
            },
            "loggers": {
                "meet": {
                    "handlers": ["console"],
                    "level": "DEBUG",
                },
            },
        }
    )
    PASSWORD_HASHERS = [
        "django.contrib.auth.hashers.MD5PasswordHasher",
    ]
    USE_SWAGGER = True
    EXTERNAL_API_ENABLED = True

    LIVEKIT_CONFIGURATION = {
        "api_key": "devkey-padded-for-minimum-len!-livekit",
        "api_secret": "secret-key-padded-for-minimum-len!-livekit",
        "url": "http://127.0.0.1.nip.io:7880",
    }

    APPLICATION_ENABLED = True
    APPLICATION_JWT_SECRET_KEY = "secret-key-padded-for-minimum-len!-application"  # noqa:S105
    APPLICATION_JWT_AUDIENCE = "Test inc."

    CELERY_TASK_ALWAYS_EAGER = True
    FILE_UPLOAD_ENABLED = True

    ADDONS_ENABLED = True
    ADDONS_CSRF_SECRET = "secret-key-padded-for-minimum-len!-addons"  # noqa:S105
    ADDONS_TOKEN_SECRET_KEY = "secret-key-padded-for-minimum-len!-addons"  # noqa:S105

    CONNECTION_TEST_ENABLED = True

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


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
    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_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"

    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": values.Value(
                "redis://redis:6379/1",
                environ_name="REDIS_URL",
                environ_prefix=None,
            ),
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            },
        },
    }


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 Sandbox(Production):
    """
    Sandbox 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.
    """


class Demo(Production):
    """
    Demonstration environment settings

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

    STORAGES = {
        "default": {
            "BACKEND": "django.core.files.storage.FileSystemStorage",
        },
        "staticfiles": {
            "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
        },
    }
