"""
Django settings schema from django_settings.yaml.

This is the SST for Django configuration. All settings flow through
this schema - no hardcoded values in Python.

SST File: lightwave/schema/definitions/django_settings.yaml

Usage:
    from lightwave.schema.pydantic.models.django_settings import DjangoSettings

    # Load settings with environment variable overrides
    settings = DjangoSettings.load()

    # Access typed settings
    print(settings.auth.user_model)  # "users.CustomUser"
    print(settings.get_django_settings())  # Dict for Django settings module
"""

import os
from functools import lru_cache
from pathlib import Path
from typing import Any, ClassVar

import yaml
from pydantic import BaseModel, Field


class SettingValue(BaseModel):
    """A single setting value with optional env override."""

    value: Any | None = None
    default: Any | None = None
    env_var: str | None = None
    type: str | None = None
    required_in_production: bool = False
    production: Any | None = None  # Production-specific override
    description: str | None = None

    def resolve(self, is_production: bool = False) -> Any:
        """
        Resolve the setting value with environment variable override.

        Priority:
        1. Environment variable (if env_var is set)
        2. Production value (if is_production and production is set)
        3. Explicit value
        4. Default value
        """
        # Check environment variable first
        if self.env_var:
            env_value = os.environ.get(self.env_var)
            if env_value is not None:
                return self._cast(env_value)

        # Check production override
        if is_production and self.production is not None:
            return self.production

        # Use explicit value or default
        if self.value is not None:
            return self.value

        return self.default

    def _cast(self, value: str) -> Any:
        """Cast string env value to appropriate type."""
        if self.type == "boolean":
            return value.lower() in ("true", "1", "yes")
        if self.type == "list":
            return [v.strip() for v in value.split(",") if v.strip()]
        if self.type == "int":
            return int(value)
        return value


class CoreSettings(BaseModel):
    """Core Django settings."""

    secret_key: SettingValue = Field(default_factory=SettingValue)
    debug: SettingValue = Field(default_factory=SettingValue)
    allowed_hosts: SettingValue = Field(default_factory=SettingValue)


class AuthSettings(BaseModel):
    """Authentication settings."""

    user_model: SettingValue = Field(default_factory=SettingValue)
    login_url: SettingValue = Field(default_factory=SettingValue)
    login_redirect_url: SettingValue = Field(default_factory=SettingValue)
    password_validators: SettingValue = Field(default_factory=SettingValue)
    authentication_backends: SettingValue = Field(default_factory=SettingValue)
    sso_allowed_domains: SettingValue = Field(default_factory=SettingValue)


class AllAuthSettings(BaseModel):
    """Django-allauth settings."""

    account_adapter: SettingValue = Field(default_factory=SettingValue)
    headless_adapter: SettingValue = Field(default_factory=SettingValue)
    socialaccount_adapter: SettingValue = Field(default_factory=SettingValue)
    account_login_methods: SettingValue = Field(default_factory=SettingValue)
    account_signup_fields: SettingValue = Field(default_factory=SettingValue)
    account_email_subject_prefix: SettingValue = Field(default_factory=SettingValue)
    account_email_unknown_accounts: SettingValue = Field(default_factory=SettingValue)
    account_confirm_email_on_get: SettingValue = Field(default_factory=SettingValue)
    account_unique_email: SettingValue = Field(default_factory=SettingValue)
    account_signup_form_honeypot_field: SettingValue = Field(default_factory=SettingValue)
    account_session_remember: SettingValue = Field(default_factory=SettingValue)
    account_logout_on_get: SettingValue = Field(default_factory=SettingValue)
    account_login_on_email_confirmation: SettingValue = Field(default_factory=SettingValue)
    account_login_by_code_enabled: SettingValue = Field(default_factory=SettingValue)
    account_forms: SettingValue = Field(default_factory=SettingValue)
    account_email_verification: SettingValue = Field(default_factory=SettingValue)
    frontend_address: SettingValue = Field(default_factory=SettingValue)
    use_headless_urls: SettingValue = Field(default_factory=SettingValue)


class I18nSettings(BaseModel):
    """Internationalization settings."""

    language_code: SettingValue = Field(default_factory=SettingValue)
    time_zone: SettingValue = Field(default_factory=SettingValue)
    use_i18n: SettingValue = Field(default_factory=SettingValue)
    use_tz: SettingValue = Field(default_factory=SettingValue)


class StaticSettings(BaseModel):
    """Static and media file settings."""

    static_url: SettingValue = Field(default_factory=SettingValue)
    media_url: SettingValue = Field(default_factory=SettingValue)


class SecuritySettings(BaseModel):
    """Security-related settings."""

    csrf_cookie_secure: SettingValue = Field(default_factory=SettingValue)
    session_cookie_secure: SettingValue = Field(default_factory=SettingValue)
    secure_ssl_redirect: SettingValue = Field(default_factory=SettingValue)
    secure_hsts_seconds: SettingValue = Field(default_factory=SettingValue)
    secure_hsts_include_subdomains: SettingValue = Field(default_factory=SettingValue)
    secure_hsts_preload: SettingValue = Field(default_factory=SettingValue)


class DjangoSettings(BaseModel):
    """
    Complete Django settings from SST YAML.

    This is the single source of truth for Django configuration.
    All values are defined in django_settings.yaml and loaded here.
    """

    SST_KEY: ClassVar[str] = "django_settings"  # Registry key in __index.yaml

    core: CoreSettings = Field(default_factory=CoreSettings)
    auth: AuthSettings = Field(default_factory=AuthSettings)
    allauth: AllAuthSettings = Field(default_factory=AllAuthSettings)
    i18n: I18nSettings = Field(default_factory=I18nSettings)
    static: StaticSettings = Field(default_factory=StaticSettings)
    security: SecuritySettings = Field(default_factory=SecuritySettings)

    @classmethod
    def get_sst_path(cls) -> Path:
        """Get path to SST YAML file (from __index.yaml registry)."""
        from lightwave.schema.core.paths import get_sst_path

        return get_sst_path(cls.SST_KEY)

    @classmethod
    @lru_cache(maxsize=1)
    def load(cls) -> "DjangoSettings":
        """
        Load settings from YAML with environment variable overrides.

        Returns:
            DjangoSettings instance with all settings resolved.
        """
        sst_path = cls.get_sst_path()
        if not sst_path.exists():
            raise FileNotFoundError(f"Django settings SST not found: {sst_path}")

        with open(sst_path) as f:
            raw_data = yaml.safe_load(f)

        # Remove _meta key
        data = {k: v for k, v in raw_data.items() if not k.startswith("_")}

        # Convert nested dicts to SettingValue objects
        processed = {}
        for section_name, section_data in data.items():
            if isinstance(section_data, dict):
                processed[section_name] = {
                    key: SettingValue(**val) if isinstance(val, dict) else SettingValue(value=val)
                    for key, val in section_data.items()
                }

        return cls(**processed)

    def get_django_settings(self, is_production: bool = False) -> dict[str, Any]:
        """
        Get settings as a dict suitable for Django settings module.

        Args:
            is_production: Whether this is production environment

        Returns:
            Dict of Django settings with resolved values
        """
        settings = {}

        # Core settings
        settings["SECRET_KEY"] = self.core.secret_key.resolve(is_production)
        settings["DEBUG"] = self.core.debug.resolve(is_production)
        settings["ALLOWED_HOSTS"] = self.core.allowed_hosts.resolve(is_production) or []

        # Auth settings
        settings["AUTH_USER_MODEL"] = self.auth.user_model.resolve(is_production)
        settings["LOGIN_URL"] = self.auth.login_url.resolve(is_production)
        settings["LOGIN_REDIRECT_URL"] = self.auth.login_redirect_url.resolve(is_production)
        settings["AUTHENTICATION_BACKENDS"] = tuple(self.auth.authentication_backends.resolve(is_production) or [])

        # Password validators
        validators = self.auth.password_validators.resolve(is_production) or []
        settings["AUTH_PASSWORD_VALIDATORS"] = [
            {"NAME": v["name"]} if isinstance(v, dict) else {"NAME": v} for v in validators
        ]

        # SSO domains
        settings["SSO_ALLOWED_DOMAINS"] = self.auth.sso_allowed_domains.resolve(is_production) or []

        # Allauth settings
        settings["ACCOUNT_ADAPTER"] = self.allauth.account_adapter.resolve(is_production)
        settings["HEADLESS_ADAPTER"] = self.allauth.headless_adapter.resolve(is_production)
        settings["SOCIALACCOUNT_ADAPTER"] = self.allauth.socialaccount_adapter.resolve(is_production)
        settings["ACCOUNT_LOGIN_METHODS"] = set(self.allauth.account_login_methods.resolve(is_production) or [])
        settings["ACCOUNT_SIGNUP_FIELDS"] = self.allauth.account_signup_fields.resolve(is_production)
        settings["ACCOUNT_EMAIL_SUBJECT_PREFIX"] = self.allauth.account_email_subject_prefix.resolve(is_production)
        settings["ACCOUNT_EMAIL_UNKNOWN_ACCOUNTS"] = self.allauth.account_email_unknown_accounts.resolve(is_production)
        settings["ACCOUNT_CONFIRM_EMAIL_ON_GET"] = self.allauth.account_confirm_email_on_get.resolve(is_production)
        settings["ACCOUNT_UNIQUE_EMAIL"] = self.allauth.account_unique_email.resolve(is_production)
        settings["ACCOUNT_SIGNUP_FORM_HONEYPOT_FIELD"] = self.allauth.account_signup_form_honeypot_field.resolve(
            is_production
        )
        settings["ACCOUNT_SESSION_REMEMBER"] = self.allauth.account_session_remember.resolve(is_production)
        settings["ACCOUNT_LOGOUT_ON_GET"] = self.allauth.account_logout_on_get.resolve(is_production)
        settings["ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION"] = self.allauth.account_login_on_email_confirmation.resolve(
            is_production
        )
        settings["ACCOUNT_LOGIN_BY_CODE_ENABLED"] = self.allauth.account_login_by_code_enabled.resolve(is_production)
        settings["ACCOUNT_FORMS"] = self.allauth.account_forms.resolve(is_production) or {}
        settings["ACCOUNT_EMAIL_VERIFICATION"] = self.allauth.account_email_verification.resolve(is_production)
        settings["FRONTEND_ADDRESS"] = self.allauth.frontend_address.resolve(is_production)
        settings["USE_HEADLESS_URLS"] = self.allauth.use_headless_urls.resolve(is_production)

        # I18n settings
        settings["LANGUAGE_CODE"] = self.i18n.language_code.resolve(is_production)
        settings["TIME_ZONE"] = self.i18n.time_zone.resolve(is_production)
        settings["USE_I18N"] = self.i18n.use_i18n.resolve(is_production)
        settings["USE_TZ"] = self.i18n.use_tz.resolve(is_production)

        # Static settings
        settings["STATIC_URL"] = self.static.static_url.resolve(is_production)
        settings["MEDIA_URL"] = self.static.media_url.resolve(is_production)

        # Security settings
        settings["CSRF_COOKIE_SECURE"] = self.security.csrf_cookie_secure.resolve(is_production)
        settings["SESSION_COOKIE_SECURE"] = self.security.session_cookie_secure.resolve(is_production)
        settings["SECURE_SSL_REDIRECT"] = self.security.secure_ssl_redirect.resolve(is_production)
        settings["SECURE_HSTS_SECONDS"] = self.security.secure_hsts_seconds.resolve(is_production)
        settings["SECURE_HSTS_INCLUDE_SUBDOMAINS"] = self.security.secure_hsts_include_subdomains.resolve(is_production)
        settings["SECURE_HSTS_PRELOAD"] = self.security.secure_hsts_preload.resolve(is_production)

        return settings

    @classmethod
    def clear_cache(cls) -> None:
        """Clear the cached settings (useful for testing)."""
        cls.load.cache_clear()
