"""
Django settings for DataSpace project.

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

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

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

import os
from pathlib import Path

import environ
import structlog
from decouple import config

# Import authorization settings
from authorization.keycloak_settings import *

from .cache_settings import *

env = environ.Env(DEBUG=(bool, False))
DEBUG = env.bool("DEBUG", default=True)
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))

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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env(
    "SECRET_KEY",
    default="django-insecure-af%#657wf@f-hclfimni96qy07fyhjbglc!@^98jpn9z%#9b6%",
)

# load env and variables
DB_ENGINE = env("DB_ENGINE", default="django.db.backends.sqlite3")
DB_NAME = env("DB_NAME", default=str(BASE_DIR / "db.sqlite3"))
DB_USER = env("DB_USER", default="DB_USER")
DB_PASSWORD = env("DB_PASSWORD", default="DB_PASSWORD")
DB_HOST = env("DB_HOST", default="DB_HOST")
DB_PORT = env("DB_PORT", default="DB_PORT")

# Data indexing database
DATA_DB_NAME = env("DATA_DB_NAME", default=str(BASE_DIR / "data.sqlite3"))
DATA_DB_USER = env("DATA_DB_USER", default=DB_USER)
DATA_DB_PASSWORD = env("DATA_DB_PASSWORD", default=DB_PASSWORD)
DATA_DB_HOST = env("DATA_DB_HOST", default=DB_HOST)
DATA_DB_PORT = env("DATA_DB_PORT", default=DB_PORT)

# SECURITY WARNING: don't run with debug turned on in production!

whitelisted_urls = env("URL_WHITELIST").split(",")
ALLOWED_HOSTS = [
    "localhost",  # Allow localhost without port
    "127.0.0.1",  # Allow IPv4 loopback
    "::1",  # Allow IPv6 loopback
] + [
    # Extract domains from whitelisted URLs
    url.replace("https://", "").replace("http://", "").split(":")[0]
    for url in whitelisted_urls
]


CSRF_TRUSTED_ORIGINS = whitelisted_urls

# Explicitly disable automatic URL normalization to prevent redirects
APPEND_SLASH = False

# Disable trailing slash redirects for GraphQL
STRICT_URL_HANDLING = True

# Disable HTTPS redirects - this is critical
SECURE_SSL_REDIRECT = False
SECURITY_MIDDLEWARE_REDIRECT_HTTPS = False
SECURE_PROXY_SSL_HEADER = None
# Maximally permissive CORS settings to fix issues
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = ["*"]
CORS_ALLOW_HEADERS = ["*"]
CORS_EXPOSE_HEADERS = ["*"]

# Apply CORS to all URLs including redirects
CORS_URLS_REGEX = r".*"

# CORS preflight settings
CORS_PREFLIGHT_MAX_AGE = 86400
# Common CORS settings
CORS_ALLOW_CREDENTIALS = True
# CORS_ALLOW_METHODS = [
#     "DELETE",
#     "GET",
#     "OPTIONS",
#     "PATCH",
#     "POST",
#     "PUT",
# ]

# CORS_ALLOW_HEADERS = [
#     "accept",
#     "accept-encoding",
#     "authorization",
#     "content-type",
#     "dnt",
#     "origin",
#     "user-agent",
#     "x-csrftoken",
#     "x-requested-with",
#     "referer",
#     "organization",
#     "dataspace",
#     "token",
#     "x-keycloak-token",  # Add Keycloak token header
# ]
# Application definition

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "corsheaders",
    "authorization.apps.AuthorizationConfig",
    "api.apps.ApiConfig",
    "strawberry_django",
    "rest_framework",
    "rest_framework_simplejwt",
    "django_elasticsearch_dsl",
    "django_elasticsearch_dsl_drf",
    "actstream",
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",  # CORS middleware must be first
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "api.utils.middleware.ContextMiddleware",
    "api.middleware.request_validator.RequestValidationMiddleware",
    "api.middleware.logging.StructuredLoggingMiddleware",
]

# Add debug toolbar middleware if in debug mode
if DEBUG:
    MIDDLEWARE.insert(1, "debug_toolbar.middleware.DebugToolbarMiddleware")

MIDDLEWARE += [
    "api.middleware.rate_limit.rate_limit_middleware",
    "authorization.middleware.KeycloakAuthenticationMiddleware",
    "authorization.middleware.activity_consent.ActivityConsentMiddleware",
]

ROOT_URLCONF = "DataSpace.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
        },
    },
]

WSGI_APPLICATION = "DataSpace.wsgi.application"

STRAWBERRY_DJANGO = {
    "FIELD_DESCRIPTION_FROM_HELP_TEXT": True,
    "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True,
    "GENERATE_ENUMS_FROM_CHOICES": True,
    "DEFAULT_PERMISSION_CLASSES": ["authorization.graphql_permissions.AllowAny"],
}

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

DATABASES = {
    "default": {
        "ENGINE": DB_ENGINE,
        "NAME": DB_NAME,
        "USER": DB_USER,
        "PASSWORD": DB_PASSWORD,
        "HOST": DB_HOST,
        "PORT": DB_PORT,
    },
    "data_db": {
        "ENGINE": DB_ENGINE,
        "NAME": DATA_DB_NAME,
        "USER": DATA_DB_USER,
        "PASSWORD": DATA_DB_PASSWORD,
        "HOST": DATA_DB_HOST,
        "PORT": DATA_DB_PORT,
    },
}

# Password validation
# https://docs.djangoproject.com/en/4.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",
    },
]

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

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"

USE_I18N = True

USE_TZ = True

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

# This STATIC_URL setting is overridden below
MEDIA_URL = "public/"
MEDIA_ROOT = os.path.join(BASE_DIR, "files", "public")

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

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
DJANGO_ALLOW_ASYNC_UNSAFE = True

# Custom User model
AUTH_USER_MODEL = "authorization.User"

# Authentication backends
AUTHENTICATION_BACKENDS = [
    "authorization.backends.KeycloakAuthenticationBackend",
    "django.contrib.auth.backends.ModelBackend",
]
ELASTICSEARCH_DSL = {
    "default": {
        "hosts": f"http://{os.getenv('ELASTICSEARCH_USERNAME', 'elastic')}:{os.getenv('ELASTICSEARCH_PASSWORD', 'changeme')}@elasticsearch:9200",
        "http_auth": (
            os.getenv("ELASTICSEARCH_USERNAME", "elastic"),
            os.getenv("ELASTICSEARCH_PASSWORD", "changeme"),
        ),
    },
}

ELASTICSEARCH_INDEX_NAMES = {
    "search.documents.dataset_document": "dataset",
    "search.documents.usecase_document": "usecase",
    "search.documents.aimodel_document": "aimodel",
}


# DVC settings
DVC_REPO_PATH = os.path.join(BASE_DIR, "dvc")
DVC_REMOTE_NAME = os.getenv("DVC_REMOTE_NAME", None)
DVC_REMOTE_URL = os.getenv("DVC_REMOTE_URL", None)

# Django REST Framework settings
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.AllowAny",  # Allow public access by default
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "authorization.authentication.KeycloakAuthentication",
        "rest_framework_simplejwt.authentication.JWTAuthentication",
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.BasicAuthentication",
    ],
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "DEFAULT_THROTTLE_RATES": {"anon": "100/hour", "user": "1000/hour"},
    "DEFAULT_FILTER_BACKENDS": ["django_filters.rest_framework.DjangoFilterBackend"],
    "PAGE_SIZE": 10,
}

# Swagger Settings
SWAGGER_SETTINGS = {
    "SECURITY_DEFINITIONS": {
        "Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"}
    }
}

# Structured Logging Configuration
LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "json_formatter": {
            "()": structlog.stdlib.ProcessorFormatter,
            "processor": structlog.processors.JSONRenderer(),
        },
    },
    "handlers": {
        "console": {
            "class": "logging.StreamHandler",
            "formatter": "json_formatter",
        },
        "file": {
            "class": "logging.handlers.RotatingFileHandler",
            "filename": "logs/dataex.log",
            "maxBytes": 1024 * 1024 * 5,  # 5 MB
            "backupCount": 5,
            "formatter": "json_formatter",
        },
    },
    "loggers": {
        "": {
            "handlers": ["console", "file"],
            "level": "DEBUG" if DEBUG else "INFO",
            "propagate": True,
        },
    },
}

# Structlog configuration
structlog.configure(
    processors=[
        structlog.stdlib.filter_by_level,
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.stdlib.add_logger_name,
        structlog.stdlib.add_log_level,
        structlog.stdlib.PositionalArgumentsFormatter(),
        structlog.processors.StackInfoRenderer(),
        structlog.processors.format_exc_info,
        structlog.processors.UnicodeDecoder(),
        structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
    ],
    logger_factory=structlog.stdlib.LoggerFactory(),
    wrapper_class=structlog.stdlib.BoundLogger,
    cache_logger_on_first_use=True,
)

# OpenTelemetry Configuration
TELEMETRY_URL = os.getenv("TELEMETRY_URL", "http://otel-collector:4317")
OTEL_SERVICE_NAME = "dataspace"
OTEL_RESOURCE_ATTRIBUTES = {
    "service.namespace": "dataspace",
    "deployment.environment": os.getenv("ENVIRONMENT", "development"),
}

# OpenTelemetry Endpoint Configuration
OTEL_EXPORTER_OTLP_ENDPOINT = TELEMETRY_URL
OTEL_EXPORTER_OTLP_PROTOCOL = "grpc"

# OpenTelemetry Sampling Configuration
OTEL_TRACES_SAMPLER = "parentbased_traceidratio"
OTEL_TRACES_SAMPLER_ARG = os.getenv(
    "OTEL_TRACES_SAMPLER_ARG", "1.0"
)  # Sample 100% in dev

# OpenTelemetry Metrics Configuration
OTEL_METRIC_EXPORT_INTERVAL_MILLIS = int(
    os.getenv("OTEL_METRIC_EXPORT_INTERVAL_MILLIS", "30000")
)
OTEL_METRIC_EXPORT_TIMEOUT_MILLIS = int(
    os.getenv("OTEL_METRIC_EXPORT_TIMEOUT_MILLIS", "30000")
)

# OpenTelemetry Instrumentation Configuration
OTEL_PYTHON_DJANGO_INSTRUMENT = True
OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS = [
    "path",
    "method",
    "scheme",
    "server_port",
    "remote_addr",
]
OTEL_PYTHON_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST = [
    "content-type",
    "user-agent",
    "x-request-id",
]
OTEL_PYTHON_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE = [
    "content-type",
    "content-length",
]

# OpenTelemetry Instrumentors
OTEL_INSTRUMENTATION_PACKAGES = [
    "elasticsearch",
    "requests",
    "redis",
    "sqlalchemy",
]

# Rate limiting settings
RATELIMIT_ENABLE = True
RATELIMIT_USE_CACHE = "default"
RATELIMIT_VIEW = "api.views.rate_limit_exceeded_view"

# Security settings
SECURE_BROWSER_XSS_FILTER = True
# Disable content type sniffing to fix MIME type issues
SECURE_CONTENT_TYPE_NOSNIFF = False
X_FRAME_OPTIONS = "DENY"
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

if not DEBUG:
    #     SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True


# Static files configuration
# Make sure this is an absolute path
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")

# Make sure the URL starts and ends with a slash
STATIC_URL = "/static/"

# Additional locations of static files - where Django will look for static files
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

# Make sure Django can find admin static files
STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

# Always use WhiteNoise for static files in both development and production
# Insert WhiteNoise middleware after security middleware
MIDDLEWARE.insert(
    MIDDLEWARE.index("django.middleware.security.SecurityMiddleware") + 1,
    "whitenoise.middleware.WhiteNoiseMiddleware",
)

# Use the simplest WhiteNoise storage configuration to avoid MIME type issues
STATICFILES_STORAGE = "whitenoise.storage.StaticFilesStorage"

# Disable compression to avoid MIME type issues
WHITENOISE_ENABLE_COMPRESSION = False

# Don't add content-type headers (let the browser determine them)
WHITENOISE_ADD_HEADERS = False

# Don't use the manifest feature which can cause issues with file references
WHITENOISE_USE_FINDERS = True

# Don't use the root directory feature which can cause conflicts
# WHITENOISE_ROOT = os.path.join(BASE_DIR, "static")

# Django Activity Stream settings
ACTSTREAM_SETTINGS = {
    "MANAGER": "actstream.managers.ActionManager",
    "FETCH_RELATIONS": True,
    "USE_PREFETCH": True,
    "USE_JSONFIELD": True,
    "GFK_FETCH_DEPTH": 1,
}

# Activity Stream Consent Settings
ACTIVITY_CONSENT = {
    # If True, user consent is required for activity tracking
    # If False, consent is assumed and all activities are tracked
    "REQUIRE_CONSENT": env.bool("ACTIVITY_REQUIRE_CONSENT", default=True),
    # Default consent setting for new users
    "DEFAULT_CONSENT": env.bool("ACTIVITY_DEFAULT_CONSENT", default=False),
    # If True, anonymous activities are tracked (when consent is not required)
    "TRACK_ANONYMOUS": env.bool("ACTIVITY_TRACK_ANONYMOUS", default=False),
    # Maximum age of activities to keep (in days, 0 means keep forever)
    "MAX_AGE_DAYS": env.int("ACTIVITY_MAX_AGE_DAYS", default=0),
}
