"""
Cache configuration schemas from cache.yaml.

These schemas define the structure for cache backends, timeouts,
invalidation patterns, and Redis configuration.

SST File: lightwave/schema/definitions/config/django/cache.yaml
Database Model: None (config-driven, not DB-backed)

Usage:
    from lightwave.schema.pydantic.models.cache import (
        CacheConfigSchema,
        CacheBackendSchema,
    )

    # Load all cache config
    config = CacheConfigSchema.get_from_sst()
    print(config.backends.default.timeout_seconds)  # 900

    # Get Django CACHES format
    caches = config.get_django_caches(redis_url="redis://localhost:6379/0")
"""

from pathlib import Path
from typing import Any, ClassVar

import yaml
from pydantic import Field

from lightwave.schema.pydantic.core.base import SSTSchema


class CacheBackendSchema(SSTSchema):
    """Individual cache backend configuration."""

    key_prefix: str = Field(..., min_length=1, description="Redis key prefix for this backend")
    timeout_seconds: int = Field(..., ge=0, description="Default timeout in seconds")
    description: str | None = Field(None, description="Human-readable description")

    def get_timeout(self) -> int:
        """Get timeout in seconds (for Django CACHES TIMEOUT)."""
        return self.timeout_seconds


class CacheTimeoutSchema(SSTSchema):
    """Named timeout preset."""

    seconds: int = Field(..., ge=0, description="Timeout duration in seconds")
    description: str | None = Field(None, description="Human-readable description")


class CacheInvalidationPatternSchema(SSTSchema):
    """Cache invalidation pattern definition."""

    pattern: str = Field(..., description="Key pattern to invalidate (supports wildcards)")
    backends: list[str] = Field(default_factory=list, description="Backends to invalidate")


class CacheBackendsSchema(SSTSchema):
    """All cache backend definitions."""

    default: CacheBackendSchema
    pages: CacheBackendSchema | None = None
    api: CacheBackendSchema | None = None
    sessions: CacheBackendSchema | None = None
    rate_limits: CacheBackendSchema | None = None


class CacheTimeoutsSchema(SSTSchema):
    """Named timeout presets."""

    realtime: CacheTimeoutSchema | None = None
    ephemeral: CacheTimeoutSchema | None = None
    brief: CacheTimeoutSchema | None = None
    standard: CacheTimeoutSchema | None = None
    extended: CacheTimeoutSchema | None = None
    hourly: CacheTimeoutSchema | None = None
    daily: CacheTimeoutSchema | None = None
    weekly: CacheTimeoutSchema | None = None


class RedisConfigSchema(SSTSchema):
    """Redis connection configuration."""

    url_env_var: str = Field("REDIS_URL", description="Environment variable for Redis URL")
    tls_url_env_var: str = Field("REDIS_TLS_URL", description="Environment variable for TLS URL")
    fallback_host: str = Field("redis", description="Fallback host if no URL provided")
    fallback_port: int = Field(6379, description="Fallback port if no URL provided")
    default_db: int = Field(0, ge=0, description="Default database number")

    max_connections: int = Field(50, ge=1, description="Maximum connections in pool")
    socket_timeout_seconds: int = Field(5, ge=1, description="Socket timeout")
    socket_connect_timeout_seconds: int = Field(5, ge=1, description="Connection timeout")

    retry_on_timeout: bool = Field(True, description="Retry on timeout")
    health_check_interval_seconds: int = Field(30, ge=0, description="Health check interval")


class CacheDevelopmentSchema(SSTSchema):
    """Development-specific cache settings."""

    use_dummy_cache: bool = Field(True, description="Use DummyCache in DEBUG mode")
    reason: str | None = Field(None, description="Why this setting exists")


class CacheFeaturesSchema(SSTSchema):
    """Cache feature flags."""

    warm_on_startup: bool = Field(False, description="Warm cache on startup")
    warm_on_startup_backends: list[str] = Field(default_factory=list, description="Backends to warm")

    compression_enabled: bool = Field(True, description="Enable compression for large values")
    compression_threshold_bytes: int = Field(1024, ge=0, description="Compression threshold")

    stats_enabled: bool = Field(True, description="Enable statistics collection")
    stats_sample_rate: float = Field(0.1, ge=0, le=1, description="Stats sampling rate")


class CacheConfigMeta(SSTSchema):
    """Metadata for cache configuration."""

    version: str = Field(..., description="Schema version")
    description: str = Field(..., description="Configuration description")
    model: str | None = Field(None, description="Database model (null for config-only)")
    sst_key: str = Field("cache", description="SST registry key")


class CacheConfigSchema(SSTSchema):
    """
    Complete cache configuration from cache.yaml.

    This is the root schema that contains all cache-related configuration.

    Example:
        config = CacheConfigSchema.get_from_sst()
        print(config.backends.default.timeout_seconds)  # 900
        print(config.timeouts.hourly.seconds)  # 3600
    """

    SST_KEY: ClassVar[str] = "cache"

    # Metadata
    meta: CacheConfigMeta = Field(..., alias="_meta", description="Configuration metadata")

    # Cache backends
    backends: CacheBackendsSchema = Field(..., description="Cache backend definitions")

    # Timeout presets
    timeouts: CacheTimeoutsSchema | None = Field(None, description="Named timeout presets")

    # Invalidation patterns
    invalidation_patterns: dict[str, CacheInvalidationPatternSchema] = Field(
        default_factory=dict, description="Cache invalidation patterns"
    )

    # Redis config
    redis: RedisConfigSchema | None = Field(None, description="Redis configuration")

    # Development settings
    development: CacheDevelopmentSchema | None = Field(None, description="Development settings")

    # Feature flags
    features: CacheFeaturesSchema | None = Field(None, description="Feature flags")

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

        return get_sst_path("cache")

    @classmethod
    def load_sst_data(cls) -> dict[str, Any]:
        """Load SST YAML file."""
        sst_path = cls.get_sst_path()
        if not sst_path.exists():
            raise FileNotFoundError(f"SST file not found: {sst_path}")
        with open(sst_path) as f:
            return yaml.safe_load(f)

    @classmethod
    def get_from_sst(cls) -> "CacheConfigSchema":
        """
        Load cache configuration from SST YAML.

        Returns:
            CacheConfigSchema instance with all cache config
        """
        data = cls.load_sst_data()

        # Get only fields defined in this schema
        known_fields = set(cls.model_fields.keys())
        known_fields.add("_meta")  # Include aliased field

        # Filter to only known fields
        filtered_data = {k: v for k, v in data.items() if k in known_fields or k == "_meta"}

        return cls(**filtered_data)

    def get_timeout(self, preset: str) -> int:
        """
        Get timeout by preset name.

        Args:
            preset: Timeout preset name (e.g., 'standard', 'hourly')

        Returns:
            Timeout in seconds

        Raises:
            ValueError: If preset doesn't exist
        """
        if not self.timeouts:
            raise ValueError("No timeout presets defined in SST")

        timeout = getattr(self.timeouts, preset, None)
        if timeout is None:
            raise ValueError(f"Unknown timeout preset: {preset}")

        return timeout.seconds

    def get_django_caches(self, *, redis_url: str) -> dict[str, dict]:
        """
        Generate Django CACHES configuration dict.

        Args:
            redis_url: Redis connection URL

        Returns:
            Dict suitable for Django CACHES setting
        """
        caches = {}

        # Add default backend
        caches["default"] = {
            "BACKEND": "django.core.cache.backends.redis.RedisCache",
            "LOCATION": redis_url,
            "KEY_PREFIX": self.backends.default.key_prefix,
            "TIMEOUT": self.backends.default.timeout_seconds,
        }

        # Add optional backends
        if self.backends.pages:
            caches["pages"] = {
                "BACKEND": "django.core.cache.backends.redis.RedisCache",
                "LOCATION": redis_url,
                "KEY_PREFIX": self.backends.pages.key_prefix,
                "TIMEOUT": self.backends.pages.timeout_seconds,
            }

        if self.backends.api:
            caches["api"] = {
                "BACKEND": "django.core.cache.backends.redis.RedisCache",
                "LOCATION": redis_url,
                "KEY_PREFIX": self.backends.api.key_prefix,
                "TIMEOUT": self.backends.api.timeout_seconds,
            }

        if self.backends.sessions:
            caches["sessions"] = {
                "BACKEND": "django.core.cache.backends.redis.RedisCache",
                "LOCATION": redis_url,
                "KEY_PREFIX": self.backends.sessions.key_prefix,
                "TIMEOUT": self.backends.sessions.timeout_seconds,
            }

        if self.backends.rate_limits:
            caches["rate_limits"] = {
                "BACKEND": "django.core.cache.backends.redis.RedisCache",
                "LOCATION": redis_url,
                "KEY_PREFIX": self.backends.rate_limits.key_prefix,
                "TIMEOUT": self.backends.rate_limits.timeout_seconds,
            }

        return caches

    def get_dummy_caches(self) -> dict[str, dict]:
        """
        Generate Django CACHES configuration for development (DummyCache).

        Returns:
            Dict with DummyCache for all backends
        """
        dummy = {"BACKEND": "django.core.cache.backends.dummy.DummyCache"}
        caches = {"default": dummy}

        if self.backends.pages:
            caches["pages"] = dummy
        if self.backends.api:
            caches["api"] = dummy
        if self.backends.sessions:
            caches["sessions"] = dummy
        if self.backends.rate_limits:
            caches["rate_limits"] = dummy

        return caches
