"""
Django settings for config project.

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

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

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

from pathlib import Path
import contextlib
import os
import sys
import ipaddress
import socket
from django.utils.translation import gettext_lazy as _
from celery.schedules import crontab
from django.http import request as http_request
from django.middleware.csrf import CsrfViewMiddleware
from django.core.exceptions import DisallowedHost
from urllib.parse import urlsplit
import django.utils.encoding as encoding

if not hasattr(encoding, "force_text"):  # pragma: no cover - Django>=5 compatibility
    from django.utils.encoding import force_str

    encoding.force_text = force_str


_original_validate_host = http_request.validate_host


def _validate_host_with_subnets(host, allowed_hosts):
    try:
        ip = ipaddress.ip_address(host)
    except ValueError:
        return _original_validate_host(host, allowed_hosts)
    for pattern in allowed_hosts:
        try:
            network = ipaddress.ip_network(pattern)
        except ValueError:
            continue
        if ip in network:
            return True
    return _original_validate_host(host, allowed_hosts)


http_request.validate_host = _validate_host_with_subnets

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

ACRONYMS: list[str] = []
with contextlib.suppress(FileNotFoundError):
    ACRONYMS = [
        line.strip()
        for line in (BASE_DIR / "config" / "data" / "ACRONYMS.txt").read_text().splitlines()
        if line.strip()
    ]


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

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-16%ed9hv^gg!jj5ff6d4w=t$50k^abkq75+vwl44%^+qyq!m#w"

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

# Enable DEBUG and related tooling when running in Terminal mode.
NODE_ROLE = os.environ.get("NODE_ROLE")
if NODE_ROLE is None:
    role_lock = BASE_DIR / "locks" / "role.lck"
    NODE_ROLE = role_lock.read_text().strip() if role_lock.exists() else "Terminal"

DEBUG = NODE_ROLE == "Terminal"

ALLOWED_HOSTS = [
    "localhost",
    "127.0.0.1",
    "testserver",
    "10.42.0.0/16",
    "192.168.0.0/16",
    "arthexis.com",
]


# Allow CSRF origin verification for hosts within allowed subnets.
_original_origin_verified = CsrfViewMiddleware._origin_verified


def _origin_verified_with_subnets(self, request):
    request_origin = request.META["HTTP_ORIGIN"]
    try:
        good_host = request.get_host()
    except DisallowedHost:
        pass
    else:
        good_origin = "%s://%s" % (
            "https" if request.is_secure() else "http",
            good_host,
        )
        if request_origin == good_origin:
            return True
        try:
            origin_host = urlsplit(request_origin).hostname
            origin_ip = ipaddress.ip_address(origin_host)
            request_ip = ipaddress.ip_address(good_host.split(":")[0])
        except ValueError:
            pass
        else:
            for pattern in ALLOWED_HOSTS:
                try:
                    network = ipaddress.ip_network(pattern)
                except ValueError:
                    continue
                if origin_ip in network and request_ip in network:
                    return True
    return _original_origin_verified(self, request)


CsrfViewMiddleware._origin_verified = _origin_verified_with_subnets


# Application definition

LOCAL_APPS = [
    "nodes",
    "core",
    "ocpp",
    "awg",
    "pages",
    "app",
]

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.admindocs",
    "config.auth_app.AuthConfig",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "import_export",
    "django_object_actions",
    "django.contrib.sites",
    "channels",
    "config.workgroup_app.WorkgroupConfig",
    "config.horologia_app.HorologiaConfig",
] + LOCAL_APPS

if DEBUG:
    try:
        import debug_toolbar  # type: ignore
    except ModuleNotFoundError:  # pragma: no cover - optional dependency
        pass
    else:
        INSTALLED_APPS += ["debug_toolbar"]

SITE_ID = 1

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "config.middleware.ActiveAppMiddleware",
    "django.middleware.locale.LocaleMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "core.middleware.AdminHistoryMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

if DEBUG:
    try:
        import debug_toolbar  # type: ignore
    except ModuleNotFoundError:  # pragma: no cover - optional dependency
        pass
    else:
        MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
        INTERNAL_IPS = ["127.0.0.1", "localhost"]

CSRF_FAILURE_VIEW = "pages.views.csrf_failure"

ROOT_URLCONF = "config.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "pages" / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.template.context_processors.i18n",
                "django.contrib.messages.context_processors.messages",
                "pages.context_processors.nav_links",
                "config.context_processors.site_and_node",
            ],
        },
    },
]

WSGI_APPLICATION = "config.wsgi.application"
ASGI_APPLICATION = "config.asgi.application"

# Channels configuration
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}}


# Custom user model
AUTH_USER_MODEL = "core.User"

# Enable RFID authentication backend and restrict default admin login to localhost
AUTHENTICATION_BACKENDS = [
    "core.backends.LocalhostAdminBackend",
    "core.backends.RFIDBackend",
]


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


def _postgres_available() -> bool:
    try:
        import psycopg
    except Exception:
        return False

    params = {
        "dbname": os.environ.get("POSTGRES_DB", "postgres"),
        "user": os.environ.get("POSTGRES_USER", "postgres"),
        "password": os.environ.get("POSTGRES_PASSWORD", ""),
        "host": os.environ.get("POSTGRES_HOST", "localhost"),
        "port": os.environ.get("POSTGRES_PORT", "5432"),
        "connect_timeout": 1,
    }
    try:
        with contextlib.closing(psycopg.connect(**params)):
            return True
    except psycopg.OperationalError:
        return False


if _postgres_available():
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.postgresql",
            "NAME": os.environ.get("POSTGRES_DB", "postgres"),
            "USER": os.environ.get("POSTGRES_USER", "postgres"),
            "PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
            "HOST": os.environ.get("POSTGRES_HOST", "localhost"),
            "PORT": os.environ.get("POSTGRES_PORT", "5432"),
            "OPTIONS": {"options": "-c timezone=UTC"},
        }
    }
else:
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.sqlite3",
            "NAME": BASE_DIR / "db.sqlite3",
            "OPTIONS": {"timeout": 30},
        }
    }


# Password validation
# https://docs.djangoproject.com/en/5.2/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/5.2/topics/i18n/

LANGUAGE_CODE = "en-us"

LANGUAGES = [
    ("en", _("English")),
    ("es", _("Spanish")),
]

LOCALE_PATHS = [BASE_DIR / "locale"]

TIME_ZONE = "America/Monterrey"

USE_I18N = True

USE_TZ = True


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

STATIC_URL = "/static/"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

# Email settings
DEFAULT_FROM_EMAIL = "arthexis@gmail.com"
SERVER_EMAIL = DEFAULT_FROM_EMAIL

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

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

# Bluesky domain account (optional)
BSKY_HANDLE = os.environ.get("BSKY_HANDLE")
BSKY_APP_PASSWORD = os.environ.get("BSKY_APP_PASSWORD")

# Logging configuration
LOG_DIR = BASE_DIR / "logs"
LOG_DIR.mkdir(exist_ok=True)
OLD_LOG_DIR = LOG_DIR / "old"
OLD_LOG_DIR.mkdir(exist_ok=True)
LOG_FILE_NAME = "tests.log" if "test" in sys.argv else f"{socket.gethostname()}.log"

LOGGING = {
    "version": 1,
    "disable_existing_loggers": False,
    "formatters": {
        "standard": {
            "format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        }
    },
    "handlers": {
        "file": {
            "class": "config.logging.ActiveAppFileHandler",
            "filename": str(LOG_DIR / LOG_FILE_NAME),
            "when": "midnight",
            "backupCount": 7,
            "encoding": "utf-8",
            "formatter": "standard",
        }
    },
    "root": {
        "handlers": ["file"],
        "level": "DEBUG",
    },
}


# Celery configuration
CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL", "memory://")
CELERY_RESULT_BACKEND = os.environ.get("CELERY_RESULT_BACKEND", "cache+memory://")
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"

CELERY_BEAT_SCHEDULE = {
    "heartbeat": {
        "task": "app.tasks.heartbeat",
        "schedule": crontab(minute="*/5"),
    }
}

