"""SAGE Configuration Settings.

Centralized configuration management using Pydantic Settings for
environment variable loading, validation, and type safety.
"""

from functools import lru_cache
from typing import Literal

from pydantic import Field, SecretStr, field_validator, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class DatabaseSettings(BaseSettings):
    """PostgreSQL database configuration."""

    model_config = SettingsConfigDict(env_prefix="POSTGRES_")

    host: str = "localhost"
    port: int = 5432
    db: str = "sage"
    user: str = "sage"
    password: SecretStr = SecretStr("")

    @property
    def url(self) -> str:
        """Generate database connection URL."""
        password = self.password.get_secret_value()
        return f"postgresql://{self.user}:{password}@{self.host}:{self.port}/{self.db}"

    @property
    def async_url(self) -> str:
        """Generate async database connection URL."""
        password = self.password.get_secret_value()
        return f"postgresql+asyncpg://{self.user}:{password}@{self.host}:{self.port}/{self.db}"


class QdrantSettings(BaseSettings):
    """Qdrant vector database configuration."""

    model_config = SettingsConfigDict(env_prefix="QDRANT_")

    host: str = "localhost"
    port: int = 6333
    grpc_port: int = 6334
    api_key: SecretStr | None = None
    collection: str = "sage_documents"
    prefer_grpc: bool = True

    @property
    def url(self) -> str:
        """Generate Qdrant connection URL."""
        return f"http://{self.host}:{self.port}"


class LLMSettings(BaseSettings):
    """LLM provider configuration."""

    # OpenAI
    openai_api_key: SecretStr | None = Field(default=None, alias="OPENAI_API_KEY")
    openai_org_id: str | None = Field(default=None, alias="OPENAI_ORG_ID")
    openai_model: str = Field(default="gpt-4-turbo-preview", alias="OPENAI_MODEL")
    openai_embedding_model: str = Field(
        default="text-embedding-3-small", alias="OPENAI_EMBEDDING_MODEL"
    )

    # Anthropic
    anthropic_api_key: SecretStr | None = Field(default=None, alias="ANTHROPIC_API_KEY")
    anthropic_model: str = Field(
        default="claude-3-sonnet-20240229", alias="ANTHROPIC_MODEL"
    )

    model_config = SettingsConfigDict(populate_by_name=True)

    @model_validator(mode="after")
    def validate_at_least_one_provider(self) -> "LLMSettings":
        """Ensure at least one LLM provider is configured in production."""
        # Validation is lenient - just checks that keys exist if provided
        return self


class SecuritySettings(BaseSettings):
    """Security-related configuration."""

    model_config = SettingsConfigDict(env_prefix="SAGE_")

    secret_key: SecretStr = SecretStr("change-me-in-production")
    cors_origins: str = "http://localhost:3000,http://localhost:8080"
    rate_limit_requests: int = 100
    rate_limit_period: int = 60  # seconds

    @property
    def cors_origins_list(self) -> list[str]:
        """Parse CORS origins into a list."""
        return [origin.strip() for origin in self.cors_origins.split(",")]

    @field_validator("secret_key", mode="after")
    @classmethod
    def warn_default_secret(cls, v: SecretStr) -> SecretStr:
        """Warn if using default secret key."""
        if v.get_secret_value() == "change-me-in-production":
            import warnings

            warnings.warn(
                "Using default SAGE_SECRET_KEY. Set a secure key in production.",
                UserWarning,
                stacklevel=2,
            )
        return v


class LoggingSettings(BaseSettings):
    """Logging configuration."""

    model_config = SettingsConfigDict(env_prefix="SAGE_")

    log_level: str = "INFO"
    audit_log_path: str = "logs/audit.jsonl"
    audit_log_rotation: str = "10MB"
    audit_log_retention: int = 90  # days

    @field_validator("log_level")
    @classmethod
    def validate_log_level(cls, v: str) -> str:
        """Validate log level is valid."""
        valid_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
        if v.upper() not in valid_levels:
            raise ValueError(f"Invalid log level: {v}. Must be one of {valid_levels}")
        return v.upper()


class MLflowSettings(BaseSettings):
    """MLflow experiment tracking configuration."""

    model_config = SettingsConfigDict(env_prefix="MLFLOW_")

    tracking_uri: str = "http://localhost:5000"
    experiment_name: str = "sage-experiments"
    artifact_root: str = "./mlruns"
    s3_endpoint_url: str | None = None


class FeatureFlags(BaseSettings):
    """Feature flags for enabling/disabling functionality."""

    model_config = SettingsConfigDict(env_prefix="SAGE_ENABLE_")

    telemetry: bool = False
    caching: bool = True
    audit_logging: bool = True
    experiment_tracking: bool = False


class ResourceLimits(BaseSettings):
    """Resource limits for the application."""

    model_config = SettingsConfigDict(env_prefix="SAGE_")

    max_context_tokens: int = 128000
    max_retrieval_chunks: int = 20
    request_timeout: int = 120  # seconds


class EscalationSettings(BaseSettings):
    """Escalation storage configuration."""

    model_config = SettingsConfigDict(env_prefix="SAGE_ESCALATION_")

    # Storage mode: "memory" or "postgres"
    storage_mode: Literal["memory", "postgres"] = "memory"

    # PostgreSQL-specific settings (used when storage_mode="postgres")
    postgres_host: str = "localhost"
    postgres_port: int = 5432
    postgres_db: str = "sage"
    postgres_user: str = "sage"
    postgres_password: SecretStr = SecretStr("")

    @property
    def postgres_url(self) -> str:
        """Generate PostgreSQL connection URL for escalation storage."""
        password = self.postgres_password.get_secret_value()
        return f"postgresql://{self.postgres_user}:{password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"

    @property
    def postgres_async_url(self) -> str:
        """Generate async PostgreSQL connection URL for escalation storage."""
        password = self.postgres_password.get_secret_value()
        return f"postgresql+asyncpg://{self.postgres_user}:{password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"


class Settings(BaseSettings):
    """Main application settings aggregating all configuration."""

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        extra="ignore",
    )

    # Environment
    env: Literal["development", "staging", "production"] = Field(
        default="development", alias="SAGE_ENV"
    )
    debug: bool = Field(default=True, alias="SAGE_DEBUG")

    # Server
    host: str = Field(default="0.0.0.0", alias="SAGE_HOST")
    port: int = Field(default=8000, alias="SAGE_PORT")
    workers: int = Field(default=4, alias="SAGE_WORKERS")

    # Sub-configurations
    database: DatabaseSettings = Field(default_factory=DatabaseSettings)
    qdrant: QdrantSettings = Field(default_factory=QdrantSettings)
    llm: LLMSettings = Field(default_factory=LLMSettings)
    security: SecuritySettings = Field(default_factory=SecuritySettings)
    logging: LoggingSettings = Field(default_factory=LoggingSettings)
    mlflow: MLflowSettings = Field(default_factory=MLflowSettings)
    features: FeatureFlags = Field(default_factory=FeatureFlags)
    limits: ResourceLimits = Field(default_factory=ResourceLimits)
    escalation: EscalationSettings = Field(default_factory=EscalationSettings)

    @property
    def is_production(self) -> bool:
        """Check if running in production environment."""
        return self.env == "production"

    @property
    def is_development(self) -> bool:
        """Check if running in development environment."""
        return self.env == "development"

    @model_validator(mode="after")
    def validate_production_settings(self) -> "Settings":
        """Validate critical settings in production."""
        if self.is_production:
            # Ensure debug is disabled in production
            if self.debug:
                raise ValueError("Debug mode must be disabled in production")

            # Ensure secret key is not default
            if (
                self.security.secret_key.get_secret_value()
                == "change-me-in-production"
            ):
                raise ValueError("Must set SAGE_SECRET_KEY in production")

            # Ensure at least one LLM provider is configured
            if not self.llm.openai_api_key and not self.llm.anthropic_api_key:
                raise ValueError(
                    "At least one LLM provider API key must be set in production"
                )

        return self


@lru_cache
def get_settings() -> Settings:
    """Get cached application settings.

    Returns:
        Settings: Application settings instance.

    Note:
        Settings are cached after first load. Use `get_settings.cache_clear()`
        to reload settings from environment.
    """
    return Settings()
