"""
Django's settings for People 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 os
from socket import gethostbyname, gethostname

from django.utils.translation import gettext_lazy as _

import sentry_sdk
from configurations import Configuration, values
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from sentry_sdk.integrations.celery import CeleryIntegration
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 = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = os.path.join("/", "data")


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(os.path.join(BASE_DIR, "version.json"), encoding="utf8") as version:
            return json.load(version)["version"]
    except FileNotFoundError:
        return "NA"  # Default: not available


def get_commit():
    """
    Get the current commit of the application
    """
    try:
        with open(os.path.join(BASE_DIR, "version.json"), encoding="utf8") as version:
            return json.load(version)["commit"]
    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:

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

    DEBUG = False
    USE_SWAGGER = values.BooleanValue(
        default=False,
        environ_name="USE_SWAGGER",
    )

    API_VERSION = "v1.0"

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

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

    # Database
    DATABASES = {
        "default": {
            "ENGINE": values.Value(
                "django.db.backends.postgresql",
                environ_name="DB_ENGINE",
                environ_prefix=None,
            ),
            "NAME": values.Value("people", environ_name="DB_NAME", environ_prefix=None),
            "USER": values.Value("dinum", environ_name="DB_USER", environ_prefix=None),
            "PASSWORD": values.Value(
                "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 = os.path.join(DATA_DIR, "static")
    MEDIA_URL = "/media/"
    MEDIA_ROOT = os.path.join(DATA_DIR, "media")

    SITE_ID = 1

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

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

    # Languages
    LANGUAGE_CODE = values.Value(
        default="en-us",
        environ_name="LANGUAGE_CODE",
        environ_prefix=None,
    )

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

    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",
        "mailbox_oauth2.middleware.one_time_email_authenticated_session",
        "oauth2_provider.middleware.OAuth2TokenMiddleware",
        "django.contrib.messages.middleware.MessageMiddleware",
        "dockerflow.django.middleware.DockerflowMiddleware",
    ]

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

    # Django's applications from the highest priority to the lowest
    INSTALLED_PLUGINS = values.ListValue(
        default=[],
        environ_name="INSTALLED_PLUGINS",
        environ_prefix=None,
    )

    INSTALLED_APPS = [
        # People
        "core",
        "demo",
        "mailbox_manager.apps.MailboxManagerConfig",
        "mailbox_oauth2",
        *INSTALLED_PLUGINS,
        # Third party apps
        "django_zxcvbn_password_validator",
        "drf_spectacular",
        "drf_spectacular_sidecar",  # required for Django collectstatic discovery
        "corsheaders",
        "django_celery_beat",
        "django_celery_results",
        "dockerflow.django",
        "easy_thumbnails",
        "oauth2_provider",
        "parler",
        "rest_framework",
        "treebeard",
        "lasuite.admin",  # must be before django.contrib.admin
        "import_export",
        # Django
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.postgres",
        "django.contrib.sessions",
        "django.contrib.sites",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        "django.contrib.admin",
        # OIDC third party
        "mozilla_django_oidc",
    ]

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

    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": (
            # "core.resource_server.authentication.ResourceServerAuthentication",
            # The resource server authentication is added on a per-view basis
            # to enforce the filtering adapted from the introspected token.
            # See ResourceServerMixin usage for more details.
            "mozilla_django_oidc.contrib.drf.OIDCAuthentication",
            "rest_framework.authentication.SessionAuthentication",
        ),
        "DEFAULT_PARSER_CLASSES": [
            "rest_framework.parsers.JSONParser",
            "nested_multipart_parser.drf.DrfNestedParser",
        ],
        "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
        "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_RATES": {
            "sustained": values.Value(
                default="150/hour",
                environ_name="SUSTAINED_THROTTLE_RATES",
                environ_prefix=None,
            ),
            "burst": values.Value(
                default="20/minute",
                environ_name="BURST_THROTTLE_RATES",
                environ_prefix=None,
            ),
        },
    }

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

    # Django Admin
    ADMIN_HEADER_BACKGROUND = values.Value(None)
    ADMIN_HEADER_COLOR = values.Value(None)
    IMPORT_EXPORT_EXPORT_PERMISSION_CODE = (
        "view"  # to prevent access outside of admin view
    )

    # 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 = values.Value(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")
    AUTH_USER_MODEL = "core.User"
    INVITATION_VALIDITY_DURATION = 604800  # 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)

    # 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_RESULT_BACKEND = "django-db"
    CELERY_CACHE_BACKEND = "django-cache"
    CELERY_BROKER_TRANSPORT_OPTIONS = values.DictValue({})
    CELERY_RESULT_EXTENDED = True
    CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 * 30  # 30 days
    CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"

    # Session
    SESSION_ENGINE = "django.contrib.sessions.backends.cache"
    SESSION_CACHE_ALIAS = "default"
    SESSION_COOKIE_AGE = 60 * 60 * 12  # 12 hours to match Agent Connect

    # Python loggers configuration (and env var overrides)
    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,
            },
        },
    }

    # 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",
    )
    OIDC_RP_SIGN_ALGO = values.Value(
        "RS256", environ_name="OIDC_RP_SIGN_ALGO", environ_prefix=None
    )
    OIDC_RP_CLIENT_ID = values.Value(
        "people", environ_name="OIDC_RP_CLIENT_ID", environ_prefix=None
    )
    OIDC_RP_CLIENT_SECRET = values.Value(
        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_INTROSPECTION_ENDPOINT = values.Value(
        environ_name="OIDC_OP_INTROSPECTION_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_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
    )
    ALLOW_LOGOUT_GET_METHOD = values.BooleanValue(
        default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", 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,
    )
    OIDC_RS_ENCRYPTION_ALGO = values.Value(
        default="RSA-OAEP",
        environ_name="OIDC_RS_ENCRYPTION_ALGO",
        environ_prefix=None,
    )
    OIDC_RS_ENCRYPTION_ENCODING = values.Value(
        default="A256GCM",
        environ_name="OIDC_RS_ENCRYPTION_ENCODING",
        environ_prefix=None,
    )

    OIDC_USERINFO_FULLNAME_FIELDS = values.ListValue(
        default=values.ListValue(  # retrocompatibility
            default=["first_name", "last_name"],
            environ_name="USER_OIDC_FIELDS_TO_FULLNAME",
            environ_prefix=None,
        ),
        environ_name="OIDC_USERINFO_FULLNAME_FIELDS",
        environ_prefix=None,
    )
    OIDC_ORGANIZATION_REGISTRATION_ID_FIELD = values.Value(
        default=None,
        environ_name="OIDC_ORGANIZATION_REGISTRATION_ID_FIELD",
        environ_prefix=None,
    )

    OIDC_OP_TOKEN_INTROSPECTION_ENDPOINT = values.Value(
        None, environ_name="OIDC_OP_TOKEN_INTROSPECTION_ENDPOINT", environ_prefix=None
    )
    OIDC_OP_URL = values.Value(None, environ_name="OIDC_OP_URL", 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_RS_AUDIENCE_CLAIM = values.Value(
        "client_id",
        environ_name="OIDC_RS_AUDIENCE_CLAIM",
        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_SIGNING_ALGO = values.Value(
        default="ES256", environ_name="OIDC_RS_SIGNING_ALGO", environ_prefix=None
    )
    OIDC_RS_SCOPES = values.ListValue(
        ["groups"], environ_name="OIDC_RS_SCOPES", environ_prefix=None
    )
    OIDC_PROXY = values.Value(None, environ_name="OIDC_PROXY", environ_prefix=None)

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

    OIDC_TIMEOUT = values.Value(None, environ_name="OIDC_TIMEOUT", environ_prefix=None)
    OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION = values.BooleanValue(
        default=True,
        environ_name="OIDC_FALLBACK_TO_EMAIL_FOR_IDENTIFICATION",
        environ_prefix=None,
    )

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

    # SUPPORT
    CRISP_WEBSITE_ID = values.Value(
        default=None,
        environ_name="CRISP_WEBSITE_ID",
        environ_prefix=None,
    )

    # MAILBOX-PROVISIONING API
    WEBMAIL_URL = values.Value(
        default=None,
        environ_name="WEBMAIL_URL",
        environ_prefix=None,
    )
    MAIL_PROVISIONING_API_URL = values.Value(
        default="http://dimail:8000",
        environ_name="MAIL_PROVISIONING_API_URL",
        environ_prefix=None,
    )
    MAIL_PROVISIONING_API_CREDENTIALS = values.Value(
        default=None,
        environ_name="MAIL_PROVISIONING_API_CREDENTIALS",
        environ_prefix=None,
    )
    MAIL_PROVISIONING_API_TIMEOUT = values.IntegerValue(
        default=20,
        environ_name="MAIL_PROVISIONING_API_TIMEOUT",
        environ_prefix=None,
    )
    MAIL_CHECK_DOMAIN_INTERVAL = values.IntegerValue(
        default=10,
        environ_name="MAIL_CHECK_DOMAIN_INTERVAL",
        environ_prefix=None,
    )
    MATRIX_BASE_HOME_SERVER = values.Value(
        default="https://matrix.agent.dinum.tchap.gouv.fr",
        environ_name="MATRIX_BASE_HOME_SERVER",
        environ_prefix=None,
    )
    MATRIX_BOT_ACCESS_TOKEN = values.Value(
        default=None,
        environ_name="MATRIX_BOT_ACCESS_TOKEN",
        environ_prefix=None,
    )

    # Organizations
    ORGANIZATION_REGISTRATION_ID_VALIDATORS = json.loads(
        values.Value(
            default="[]",
            environ_name="ORGANIZATION_REGISTRATION_ID_VALIDATORS",
            environ_prefix=None,
        )
    )
    ORGANIZATION_METADATA_SCHEMA = values.Value(
        default=None,
        environ_name="ORGANIZATION_METADATA_SCHEMA",
        environ_prefix=None,
    )

    OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth2_provider.Application"
    OAUTH2_PROVIDER_GRANT_MODEL = "mailbox_oauth2.Grant"
    OAUTH2_PROVIDER_ID_TOKEN_MODEL = "mailbox_oauth2.IDToken"  # noqa: S105
    OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "mailbox_oauth2.AccessToken"  # noqa: S105
    OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "mailbox_oauth2.RefreshToken"  # noqa: S105

    # Security settings for login attempts
    # - Maximum number of failed login attempts before lockout
    MAX_LOGIN_ATTEMPTS = values.IntegerValue(
        default=5,
        environ_name="MAX_LOGIN_ATTEMPTS",
        environ_prefix=None,
    )
    # - Lockout time in seconds (default to 5 minutes)
    ACCOUNT_LOCKOUT_TIME = values.IntegerValue(
        default=5 * 60,
        environ_name="ACCOUNT_LOCKOUT_TIME",
        environ_prefix=None,
    )

    MANAGEMENT_COMMAND_AS_TASK = [
        "fill_organization_metadata",
    ] + values.ListValue(
        default=[],
        environ_name="MANAGEMENT_COMMAND_AS_TASK",
        environ_prefix=None,
    )

    SEND_MAILBOX_PASSWORD = values.Value(
        default=False,
        environ_name="SEND_MAILBOX_PASSWORD",
        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 FEATURES(self):
        """Feature flags for the application."""
        FEATURE_FLAGS: set = {
            "CONTACTS_CREATE",  # Used in the users/me/ endpoint
            "CONTACTS_DISPLAY",  # Used in the users/me/ endpoint
            "MAILBOXES_CREATE",  # Used in the users/me/ endpoint
            "TEAMS_DISPLAY",
            "TEAMS_CREATE",  # Used in the users/me/ endpoint
        }

        return {
            feature: values.BooleanValue(
                default=True,
                environ_name=f"FEATURE_{feature}",
                environ_prefix=None,
            )
            for feature in FEATURE_FLAGS
        }

    # 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 COMMIT(self):
        """
        Return the commit information.
        """
        return get_commit()

    # 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,
            },
        }

    @property
    def OAUTH2_PROVIDER(self) -> dict:
        """OAuth2 Provider settings."""
        OIDC_ENABLED = values.BooleanValue(
            default=False,
            environ_name="OAUTH2_PROVIDER_OIDC_ENABLED",
            environ_prefix=None,
        )
        OIDC_RSA_PRIVATE_KEY = values.Value(
            environ_name="OAUTH2_PROVIDER_OIDC_RSA_PRIVATE_KEY",
            environ_prefix=None,
        )
        OAUTH2_VALIDATOR_CLASS = values.Value(
            default="mailbox_oauth2.validators.BaseValidator",
            environ_name="OAUTH2_PROVIDER_VALIDATOR_CLASS",
            environ_prefix=None,
        )
        SCOPES = {
            "openid": "OpenID Connect scope",
            "email": "Email address",
        }
        if OAUTH2_VALIDATOR_CLASS == "mailbox_oauth2.validators.ProConnectValidator":
            SCOPES["given_name"] = "First name"
            SCOPES["usual_name"] = "Last name"
            SCOPES["siret"] = "SIRET number"
            SCOPES["siren"] = "SIREN number"
            SCOPES["uid"] = "UID"
            # available but not filled
            SCOPES["organizational_unit"] = "Organizational unit"
            SCOPES["belonging_population"] = "Belonging population"
            SCOPES["phone"] = "Phone number"
            SCOPES["chorusdt"] = "Chorus DT"

        return {
            "OIDC_ENABLED": OIDC_ENABLED,
            "OIDC_RSA_PRIVATE_KEY": OIDC_RSA_PRIVATE_KEY,
            "SCOPES": SCOPES,
            "OAUTH2_VALIDATOR_CLASS": OAUTH2_VALIDATOR_CLASS,
        }

    @property
    def LOGIN_URL(self):
        """
        Define the LOGIN_URL (Django) for the OIDC provider (reuse LOGIN_REDIRECT_URL)
        """
        return f"{self.LOGIN_REDIRECT_URL}/login/"

    @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(),
                    CeleryIntegration(monitor_beat_tasks=True),
                ],
                traces_sample_rate=0.1,
            )

            # Add the application name to the Sentry scope
            scope = sentry_sdk.get_global_scope()
            scope.set_tag("application", "backend")

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

    @classmethod
    def generate_temporary_rsa_key(cls):
        """Generate a temporary RSA key for OIDC Provider."""

        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=4096,
        )

        # - Serialize private key to PEM format
        private_key_pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption(),
        )

        return private_key_pem.decode("utf-8")


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 = "people_sessionid"
    SESSION_CACHE_ALIAS = "session"

    CACHES = {
        "default": {
            "BACKEND": "django.core.cache.backends.dummy.DummyCache",
        },
        "session": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": values.Value(
                "redis://redis:6379/2",
                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",
            },
        },
    }

    # this is a dev credentials for mail provisioning API
    MAIL_PROVISIONING_API_CREDENTIALS = "bGFfcmVnaWU6cGFzc3dvcmQ="

    OIDC_ORGANIZATION_REGISTRATION_ID_FIELD = "siret"

    def __init__(self):
        """In dev, force installs needed for Swagger API."""
        # pylint: disable=invalid-name
        self.INSTALLED_APPS += ["django_extensions"]

    @property
    def OAUTH2_PROVIDER(self):
        """OAuth2 Provider settings."""
        OAUTH2_PROVIDER = super().OAUTH2_PROVIDER  # pylint: disable=invalid-name
        if not OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"]:
            OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"] = Base.generate_temporary_rsa_key()
        return OAUTH2_PROVIDER


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

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

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

    CELERY_TASK_ALWAYS_EAGER = values.BooleanValue(True)

    # this is a dev credentials for mail provisioning API
    MAIL_PROVISIONING_API_CREDENTIALS = "bGFfcmVnaWU6cGFzc3dvcmQ="

    OIDC_ORGANIZATION_REGISTRATION_ID_FIELD = "siret"

    ORGANIZATION_REGISTRATION_ID_VALIDATORS = [
        {
            "NAME": "django.core.validators.RegexValidator",
            "OPTIONS": {
                "regex": "^[0-9]{14}$",
            },
        },
    ]


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

    # Password management

    # - Password strength for ZxcvbnPasswordValidator
    # 0 too guessable: risky password. (guesses < 10^3)
    # 1 very guessable: protection from throttled online attacks. (guesses < 10^6)
    # 2 somewhat guessable: protection from unthrottled online attacks. (guesses < 10^8)
    # 3 safely unguessable: moderate protection from offline slow-hash scenario. (guesses < 10^10)
    # 4 very unguessable: strong protection from offline slow-hash scenario. (guesses >= 10^10)
    PASSWORD_MINIMAL_STRENGTH = values.IntegerValue(
        default=3,
        environ_name="PASSWORD_MINIMAL_STRENGTH",
        environ_prefix=None,
    )

    AUTH_PASSWORD_VALIDATORS = [
        {
            "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
            "OPTIONS": {
                "user_attributes": (
                    "email",  # for core.User
                    "name",  # for core.User
                    "first_name",  # for mailbox_manager.Mailbox
                    "last_name",  # for mailbox_manager.Mailbox
                    "local_part",  # for mailbox_manager.Mailbox
                ),
            },
        },
        {
            "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
        },
        {
            "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
        },
        {
            "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
        },
        {
            "NAME": "django_zxcvbn_password_validator.ZxcvbnPasswordValidator",
        },
    ]

    # For static files in production, we want to use a backend that includes a hash in
    # the filename, that is calculated from the file content, so that browsers always
    # get the updated version of each file.
    STORAGES = {
        "default": {
            "BACKEND": "storages.backends.s3.S3Storage",
        },
        "staticfiles": {
            # For static files in production, we want to use a backend that includes a hash in
            # the filename, that is calculated from the file content, so that browsers always
            # get the updated version of each file.
            "BACKEND": values.Value(
                "whitenoise.storage.CompressedManifestStaticFilesStorage",
                environ_name="STORAGES_STATICFILES_BACKEND",
            )
        },
    }

    # Privacy
    SECURE_REFERRER_POLICY = "same-origin"

    # Media
    AWS_S3_ENDPOINT_URL = values.Value()
    AWS_S3_ACCESS_KEY_ID = values.Value()
    AWS_S3_SECRET_ACCESS_KEY = values.Value()
    AWS_STORAGE_BUCKET_NAME = values.Value("tf-default-people-media-storage")
    AWS_S3_REGION_NAME = values.Value()

    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 Local(Production):
    """
    Local environment settings

    This configuration is used by the developers to run the application
    locally through the helm files (used for tilt)

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

    @property
    def OAUTH2_PROVIDER(self):
        """OAuth2 Provider settings."""
        OAUTH2_PROVIDER = super().OAUTH2_PROVIDER  # pylint: disable=invalid-name
        if not OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"]:
            OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"] = Base.generate_temporary_rsa_key()
        return OAUTH2_PROVIDER


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


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",
        },
    }
