from __future__ import absolute_import

import sys
import warnings
from pathlib import Path

from django.core.cache import CacheKeyWarning
from google.oauth2 import service_account

"""
Django settings for lex_app project.

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

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

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

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
from datetime import timedelta

import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration


def traces_sampler(sampling_context):
    if sampling_context == "/health":
        # Drop this transaction, by setting its sample rate to 0%
        return 0
    else:
        # Default sample rate for all others (replaces traces_sample_rate)
        return 0.1


if (
    os.getenv("DEPLOYMENT_ENVIRONMENT") != "DEV"
    and os.getenv("DEPLOYMENT_ENVIRONMENT") is not None
):
    sentry_sdk.init(
        dsn="https://a3aa24a7fccd42dbbb7457e6402b5443@o1318244.ingest.sentry.io/6573603",
        integrations=[
            DjangoIntegration(),
        ],
        environment=os.getenv("DEPLOYMENT_ENVIRONMENT"),
        # Set traces_sample_rate to 1.0 to capture 100%
        # of transactions for performance monitoring.
        # We recommend adjusting this value in production.
        traces_sampler=traces_sampler,
        # If you wish to associate users to errors (assuming you are using
        # django.contrib.auth) you may enable sending PII data.
        send_default_pii=True,
    )


warnings.simplefilter("ignore", CacheKeyWarning)

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
NEW_BASE_DIR = Path(os.getenv("PROJECT_ROOT", os.getcwd())).parent.as_posix()
sys.path.append(NEW_BASE_DIR)

ASGI_APPLICATION = "lex_app.asgi.application"

if os.getenv("DEPLOYMENT_ENVIRONMENT") is None:
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels.layers.InMemoryChannelLayer",
        },
    }
else:
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
            "CONFIG": {
                "hosts": [
                    (
                        f"redis://{os.getenv('REDIS_USERNAME')}:{os.getenv('REDIS_PASSWORD')}@{os.getenv('REDIS_HOST')}/2"
                        if os.getenv("DEPLOYMENT_ENVIRONMENT") is not None
                        else "redis://127.0.0.1:6379/2"
                    )
                ],
                "capacity": 100000,
                "expiry": 10,
                "prefix": f"{os.getenv('INSTANCE_RESOURCE_IDENTIFIER', 'local')}:",
            },
        },
    }

STORAGES = {
    "default": {
        "BACKEND": "lex.utilities.storage.custom_storage.CustomDefaultStorage",
    },
    "staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},

}

SHAREPOINT_APP_CLIENT_ID = os.getenv("SHAREPOINT_APP_CLIENT_ID")
SHAREPOINT_API_CERTIFICATE_PATH = os.getenv("SHAREPOINT_API_CERTIFICATE_PATH")
SHAREPOINT_API_CERTIFICATE_THUMBPRINT = os.getenv(
    "SHAREPOINT_API_CERTIFICATE_THUMBPRINT"
)
SHAREPOINT_API_TENANT_NAME = os.getenv("SHAREPOINT_API_TENANT_NAME")
SHAREPOINT_URL = os.getenv("SHAREPOINT_URL", "local")
SHAREPOINT_STATIC_DIR = "static"
SHAREPOINT_MEDIA_DIR = "uploads"

if os.getenv("STORAGE_TYPE") == "SHAREPOINT":
    STORAGES = {
        "default": {
            "BACKEND": "django_sharepoint_storage.SharePointCloudStorageUtils.Media",
        }
    }
    MEDIA_ROOT = "uploads/"

if os.getenv("STORAGE_TYPE") == "GCS":
    STORAGES = {
        "default": {
            "BACKEND": "lex.utilities.utils.gcs_utils.Media",
        }
    }
    GS_BUCKET_NAME = os.getenv("GS_BUCKET_NAME")
    GS_CREDENTIALS = service_account.Credentials.from_service_account_file(
        os.path.join(NEW_BASE_DIR, "django-storages", "gcpCredentials.json"),
    )
    MEDIA_ROOT = "uploads/"

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv(
    "DJANGO_SECRET_KEY", default="pjlulvaa77lteno-_y6!oxb%63xqiaw4%n%1or&77a!x9@nkd+"
)

# SECURITY WARNING: don't run with debug turned on in production!
if os.getenv("LEX_ENVIRONMENT_TAG", "envvar_not_existing") == "dev":
    DEBUG = True
else:
    DEBUG = False

ALLOWED_HOSTS = [
    os.getenv("DOMAIN_HOSTED", "localhost"),
    "127.0.0.1",
    "localhost",
    os.environ.get("POD_IP", default="envvar_not_existing"),
]

LOGIN_REDIRECT_URL = "/process_admin/all"

CSRF_TRUSTED_ORIGINS = [
    "https://*." + os.getenv("DOMAIN_HOSTED", "localhost"),
    "http://localhost:3000",
]

REACT_APP_BUILD_PATH = (
    Path(__file__).resolve().parent.parent / Path("react/build")
).as_posix()
repo_name = os.getenv("PROJECT_ROOT", Path(os.getcwd()).resolve().as_posix()).split(
    "/"
)[-1]
LEGACY_MEDIA_ROOT = os.path.join(NEW_BASE_DIR, f"{repo_name}/")
LOG_FILE_PATH = os.path.join(NEW_BASE_DIR, f"{repo_name}/{repo_name}.log")


LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "verbose": {
            "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}",
            "style": "{",
        },
    },
    "handlers": {
        "file": {
            "level": "DEBUG",
            "class": "logging.FileHandler",
            "filename": LOG_FILE_PATH,
            "formatter": "verbose",
        },
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "simple",
        },
        "websocket": {
            "level": "DEBUG",
            "class": "lex.audit_logging.handlers.websocket_handler.WebSocketHandler",
        },
    },
    "loggers": {
        "LexLogger": {
            "handlers": ["file"],
            "level": "DEBUG",
            "propagate": True,
        },
    },
}


# Application definition

INSTALLED_APPS = [
    "channels",
    "lex.utilities.apps.UtilitiesConfig",
    "lex.core.apps.CoreConfig",
    "lex.authentication.apps.AuthenticationConfig",
    "lex.audit_logging.apps.LoggingConfig",
    "lex.api.apps.ApiConfig",
    "lex.process_admin.apps.ProcessAdminConfig",
    "lex.lex_app.apps.LexAppConfig",
    "simple_history",
    "celery",
    "react",
    "markdown",
    "lex.lex_app.apps.CustomAdminConfig",  # Custom admin config to prevent auth admin conflicts
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "rest_framework_api_key",
    "django.contrib.postgres",
    "django_db_views",
    "oauth2_authcodeflow",
]

if not repo_name.startswith("lex"):
    INSTALLED_APPS.append(repo_name)


CRISPY_FAIL_SILENTLY = not DEBUG

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "simple_history.middleware.HistoryRequestMiddleware",
    "lex.api.middleware.keycloak_permissions.KeycloakPermissionsMiddleware",
    "oauth2_authcodeflow.middleware.LoginRequiredMiddleware",
    "oauth2_authcodeflow.middleware.RefreshSessionMiddleware",
    "oauth2_authcodeflow.middleware.RefreshAccessTokenMiddleware",
]

ROOT_URLCONF = "lex_app.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [os.path.join(BASE_DIR, "templates"), "generic_app/submodels/"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "lex_app.wsgi.application"

DISABLE_SERVER_SIDE_CURSORS = True

db_username = os.getenv("POSTGRES_USERNAME", "django")
kubernetes = not os.getenv("KUBERNETES_ENGINE", "NONE") == "NONE"

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "default_cache",
        "TIMEOUT": None,
    },
    "oidc": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "oidc_cache",
    },
    "redis": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": (
            f"redis://{os.getenv('REDIS_USERNAME')}:{os.getenv('REDIS_PASSWORD')}@{os.getenv('REDIS_HOST')}/2"
            if os.getenv("DEPLOYMENT_ENVIRONMENT") is not None
            else "redis://127.0.0.1:6379/2"
        ),
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            # Optional: silently ignore redis down-time
            "IGNORE_EXCEPTIONS": True,
        },
    },
    "local": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake",
    }

}


DATABASES = {
    "local": {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, f"{repo_name}.sqlite3"),
    },
    "default": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": f"db_{repo_name}",
        "USER": "django",
        "PASSWORD": "lundadminlocal",
        "HOST": "localhost",
        "PORT": "5432",
    },
    "GCP": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        "USER": db_username,
        "PASSWORD": os.getenv("POSTGRES_PASSWORD", "envvar_not_existing"),
        "HOST": os.getenv("DATABASE_DOMAIN", "envvar_not_existing"),
        "PORT": "5432",
        "TEST": {
            "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        },
    },
    "DOCKER-COMPOSE": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        "USER": db_username,
        "PASSWORD": os.getenv("POSTGRES_PASSWORD", "envvar_not_existing"),
        "HOST": os.getenv("DATABASE_DOMAIN", "envvar_not_existing"),
        "PORT": "5432",
        "TEST": {
            "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        },
    },
    "K8S": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        "USER": db_username,
        "PASSWORD": os.getenv("POSTGRES_PASSWORD", "envvar_not_existing"),
        "HOST": os.getenv("DATABASE_DOMAIN", "envvar_not_existing"),
        "PORT": "5432",
        "OPTIONS": {"sslmode": "disable"},
        "TEST": {
            "NAME": os.getenv("DATABASE_NAME", "envvar_not_existing"),
        },
    },
}
DATABASE_DEPLOYMENT_TARGET = os.getenv("DATABASE_DEPLOYMENT_TARGET", "local")
if DATABASE_DEPLOYMENT_TARGET != "local":
    DATABASES["default"] = DATABASES[DATABASE_DEPLOYMENT_TARGET]
else:
    DATABASES["default"] = DATABASES["local"]

MIGRATION_MODULES = {}


# CELERY STUFF
# Celery Broker Configuration - Redis
CELERY_BROKER_URL = (
    f"redis://{os.getenv('REDIS_USERNAME')}:{os.getenv('REDIS_PASSWORD')}@{os.getenv('REDIS_HOST')}/1"
    if os.getenv("DEPLOYMENT_ENVIRONMENT") is not None
    else "redis://127.0.0.1:6379/1"
)

# Celery Result Backend Configuration - Redis (changed from PostgreSQL)
CELERY_RESULT_BACKEND = (
    f"db+postgresql://{db_username}:{os.getenv('POSTGRES_PASSWORD', 'envvar_not_existing')}@{os.getenv('DATABASE_DOMAIN', 'envvar_not_existing')}/{os.getenv('DATABASE_NAME', 'envvar_not_existing')}"
    if os.getenv("DEPLOYMENT_ENVIRONMENT") is not None
    else f"db+postgresql://django:lundadminlocal@localhost/db_{repo_name}"
)

# Celery Configuration
CELERY_CACHE_BACKEND = "default"
CELERY_ACCEPT_CONTENT = ["application/json", "pickle"]
CELERY_TASK_SERIALIZER = "pickle"
CELERY_RESULT_SERIALIZER = "pickle"
CELERY_CREATE_MISSING_QUEUES = True
CELERY_TASK_TRACK_STARTED = True
CELERY_RESULT_PERSISTENT = True
CELERY_TASK_DEFAULT_QUEUE = os.getenv("INSTANCE_RESOURCE_IDENTIFIER", "celery")

# Redis-specific Celery configuration
CELERY_BROKER_TRANSPORT_OPTIONS = {
    "global_keyprefix": f"{os.getenv('INSTANCE_RESOURCE_IDENTIFIER', 'celery')}:",
    "visibility_timeout": float("inf"),
}
CELERY_RESULT_BACKEND_TRANSPORT_OPTIONS = {
    "global_keyprefix": f"{os.getenv('INSTANCE_RESOURCE_IDENTIFIER', 'celery')}:",
    "visibility_timeout": float("inf"),
}

# Task execution configuration
CELERY_TASK_ACKS_LATE = True
CELERY_TASK_REJECT_ON_WORKER_LOST = True
CELERY_WORKER_PREFETCH_MULTIPLIER = 1

# Result expiration (Redis cleanup)
CELERY_RESULT_EXPIRES = 3600  # 1 hour

# Enhanced Celery Active Configuration
# Support both CELERY_ACTIVE and legacy C_FORCE_ROOT for backward compatibility
CELERY_ACTIVE = os.getenv("CELERY_ACTIVE", "False").lower() == "true"
CELERY_TASK_TIMEOUT = int(os.getenv("CELERY_TASK_TIMEOUT", "3600"))  # 1 hour
CELERY_MAX_RETRIES = int(os.getenv("CELERY_MAX_RETRIES", "3"))
CELERY_RETRY_DELAY = int(os.getenv("CELERY_RETRY_DELAY", "60"))  # seconds

# Legacy support for C_FORCE_ROOT (backward compatibility)
try:
    c_force_root = os.getenv("C_FORCE_ROOT")
    if c_force_root == "True":
        CELERY_ACTIVE = True
    # If CELERY_ACTIVE is already True from environment, keep it True
    # Only set to False if both C_FORCE_ROOT and CELERY_ACTIVE are not set to True
    elif not CELERY_ACTIVE:
        CELERY_ACTIVE = False
except Exception as e:
    # If there's any error, fall back to the CELERY_ACTIVE environment variable value
    pass

# Make celery_active available as a module-level variable for backward compatibility
celery_active = CELERY_ACTIVE

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

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

CORS_ORIGIN_ALLOW_ALL = True

DATA_UPLOAD_MAX_NUMBER_FIELDS = None
KEYCLOAK_URL = os.getenv("KEYCLOAK_URL")
KEYCLOAK_REALM = os.getenv("KEYCLOAK_REALM")
OIDC_RP_CLIENT_ID = os.getenv("OIDC_RP_CLIENT_ID")
OIDC_RP_CLIENT_SECRET = os.getenv("OIDC_RP_CLIENT_SECRET")
OIDC_RP_CLIENT_UUID = os.getenv("OIDC_RP_CLIENT_UUID")
OIDC_OP_USER_ENDPOINT          = f"{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/userinfo"
OIDC_OP_DISCOVERY_DOCUMENT_URL = f"{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/.well-known/openid-configuration"
AUTHENTICATION_BACKENDS = (
    "oauth2_authcodeflow.auth.AuthenticationBackend",
)

OIDC_RP_SCOPES = ["openid", "email", "profile"]
OIDC_MIDDLEWARE_NO_AUTH_URL_PATTERNS = ["/health", "/favicon.ico", "api/user/"]
OIDC_RP_USE_PKCE = False
OIDC_MIDDLEWARE_LOGIN_REQUIRED_REDIRECT = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
    "http://localhost:8000",
    "*"
    # add any other hosts your front-end uses
]


JWT_SECRET_KEY = SECRET_KEY
JWT_ALGORITHM = 'HS256'
JWT_ACCESS_TOKEN_LIFETIME = timedelta(minutes=5)  # Short-lived
JWT_REFRESH_GRACE_PERIOD = timedelta(minutes=10)  # Grace period for refresh
JWT_EXPIRATION_HOURS = 2


REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        # other authentication classes, if needed
    ],
    "DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
    "TEST_REQUEST_DEFAULT_FORMAT": "json",
    # FIXME: maybe use this at some point (for giving individual access rights):
    # "DEFAULT_PERMISSION_CLASSES": ['rest_framework_api_key.permissions.HasAPIKey' | 'rest_framework.permissions.IsAuthenticated']
}
API_KEY_CUSTOM_HEADER = "HTTP_API_KEY"

if os.getenv("SENDGRID_API_KEY", "envvar_not_existing") != "envvar_not_existing":
    SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]
else:
    print("SENDGRID_API_KEY not found in environmental variables.")
    SENDGRID_API_KEY = "improperlyConfigured"

EMAIL_BACKEND = "sendgrid_backend.SendgridBackend"
SENDGRID_SANDBOX_MODE_IN_DEBUG = False
SENDGRID_ECHO_TO_STDOUT = True

# ##################################################
# ################## END AUTH ######################
# ##################################################

CORS_ORIGIN_WHITELIST = [
    "https://" + os.getenv("DOMAIN_HOSTED", "localhost:3000"),
    "http://localhost:3000",
    "http://127.0.0.1:3000",
    "http://localhost:8000",
    "*",
]

LANGUAGE_CODE = "en-us"

# TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

# TODO: does this fix the "Unauthorized: /api/model_tree/"-issue which occurs after some time??
TIME_ZONE = "Europe/Berlin"

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

STATIC_URL = "/static-django/"

STATIC_ROOT = "/static"

if os.getenv("STORAGE_TYPE") == "LEGACY" or not os.getenv("STORAGE_TYPE"):
    if os.getenv("KUBERNETES_ENGINE", "NONE") == "NONE":
        MEDIA_ROOT = os.path.join(NEW_BASE_DIR, f"{repo_name}/")
        MEDIA_URL = os.path.join(NEW_BASE_DIR, f"{repo_name}/")
        USER_REPORT_ROOT = os.path.join(NEW_BASE_DIR, f"{repo_name}/")
    else:
        # this section is not in effect when a cloud storage option is used.
        # Check DjangoProcessAdminGeneric/gcsUtils.py or
        # DjangoProcessAdminGeneric/sharepoint/SharePointCloudStorageUtils.py for replacement.
        MEDIA_ROOT = "/app/storage/uploads/"
        MEDIA_URL = "/app/storage/uploads/"

        USER_REPORT_ROOT = "/app/storage/reports/"

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        # Console stays plain
        "default": {
            "format": "{levelname} {asctime} {message}",
            "style": "{",
        },
        # Markdown formatter for WS – header in Markdown, message as-is
        "ws_md": {
            "format": ("\n{message}"),
            "style": "{",
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "default",
        },
        "ws": {
            "()": "lex.audit_logging.handlers.websocket_handler.WebSocketHandler",
            "level": "DEBUG",
            "formatter": "ws_md",
        },
    },
    "loggers": {
        "lex.calclog": {
            "handlers": ["ws", "console"],
            "level": "DEBUG",
            "propagate": False,
        },
    },
    "root": {
        "handlers": ["console"],
        "level": os.getenv("LOG_LEVEL", "DEBUG"),
    },
}

INSTANCE_CONTROLLER_BASE_URL = os.getenv(
    "INSTANCE_CONTROLLER_BASE_URL",
    "https://hazem.excellence-cloud.dev",
)

KEYCLOAK_SETUP_CALLBACK_URL = os.getenv(
    "KEYCLOAK_SETUP_CALLBACK_URL",
    "http://127.0.0.1:8000/api/bootstrap/keycloak/",
)


DATA_UPLOAD_MAX_MEMORY_SIZE = None

# Simple History Configuration
# Set to False to disable automatic exclusion of Django built-in models
SIMPLE_HISTORY_AUTO_EXCLUDE_BUILTIN = True

# Additional models to exclude from history tracking (beyond the built-in exclusions)
# Add model names (lowercase) that you specifically don't want to track
SIMPLE_HISTORY_EXCLUDED_MODELS = [
    # Example: 'temporarydata', 'cachedresult'
]
