"""
Pydantic settings for WiFi-DensePose API
"""

import os
from typing import List, Optional, Dict, Any
from functools import lru_cache

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


class Settings(BaseSettings):
    """Application settings with environment variable support."""
    
    # Application settings
    app_name: str = Field(default="WiFi-DensePose API", description="Application name")
    version: str = Field(default="1.0.0", description="Application version")
    environment: str = Field(default="development", description="Environment (development, staging, production)")
    debug: bool = Field(default=False, description="Debug mode")
    
    # Server settings
    host: str = Field(default="0.0.0.0", description="Server host")
    port: int = Field(default=8000, description="Server port")
    reload: bool = Field(default=False, description="Auto-reload on code changes")
    workers: int = Field(default=1, description="Number of worker processes")
    
    # Security settings
    secret_key: str = Field(..., description="Secret key for JWT tokens")
    jwt_algorithm: str = Field(default="HS256", description="JWT algorithm")
    jwt_expire_hours: int = Field(default=24, description="JWT token expiration in hours")
    allowed_hosts: List[str] = Field(default=["*"], description="Allowed hosts")
    cors_origins: List[str] = Field(default=["*"], description="CORS allowed origins")
    
    # Rate limiting settings
    rate_limit_requests: int = Field(default=100, description="Rate limit requests per window")
    rate_limit_authenticated_requests: int = Field(default=1000, description="Rate limit for authenticated users")
    rate_limit_window: int = Field(default=3600, description="Rate limit window in seconds")
    
    # Database settings
    database_url: Optional[str] = Field(default=None, description="Database connection URL")
    database_pool_size: int = Field(default=10, description="Database connection pool size")
    database_max_overflow: int = Field(default=20, description="Database max overflow connections")
    
    # Database connection pool settings (alternative naming for compatibility)
    db_pool_size: int = Field(default=10, description="Database connection pool size")
    db_max_overflow: int = Field(default=20, description="Database max overflow connections")
    db_pool_timeout: int = Field(default=30, description="Database pool timeout in seconds")
    db_pool_recycle: int = Field(default=3600, description="Database pool recycle time in seconds")
    
    # Database connection settings
    db_host: Optional[str] = Field(default=None, description="Database host")
    db_port: int = Field(default=5432, description="Database port")
    db_name: Optional[str] = Field(default=None, description="Database name")
    db_user: Optional[str] = Field(default=None, description="Database user")
    db_password: Optional[str] = Field(default=None, description="Database password")
    db_echo: bool = Field(default=False, description="Enable database query logging")
    
    # Redis settings (for caching and rate limiting)
    redis_url: Optional[str] = Field(default=None, description="Redis connection URL")
    redis_password: Optional[str] = Field(default=None, description="Redis password")
    redis_db: int = Field(default=0, description="Redis database number")
    redis_enabled: bool = Field(default=True, description="Enable Redis")
    redis_host: str = Field(default="localhost", description="Redis host")
    redis_port: int = Field(default=6379, description="Redis port")
    redis_required: bool = Field(default=False, description="Require Redis connection (fail if unavailable)")
    redis_max_connections: int = Field(default=10, description="Maximum Redis connections")
    redis_socket_timeout: int = Field(default=5, description="Redis socket timeout in seconds")
    redis_connect_timeout: int = Field(default=5, description="Redis connection timeout in seconds")
    
    # Failsafe settings
    enable_database_failsafe: bool = Field(default=True, description="Enable automatic SQLite failsafe when PostgreSQL unavailable")
    enable_redis_failsafe: bool = Field(default=True, description="Enable automatic Redis failsafe (disable when unavailable)")
    sqlite_fallback_path: str = Field(default="./data/wifi_densepose_fallback.db", description="SQLite fallback database path")
    
    # Hardware settings
    wifi_interface: str = Field(default="wlan0", description="WiFi interface name")
    csi_buffer_size: int = Field(default=1000, description="CSI data buffer size")
    hardware_polling_interval: float = Field(default=0.1, description="Hardware polling interval in seconds")
    router_ssh_username: str = Field(default="admin", description="Default SSH username for router connections")
    router_ssh_password: str = Field(default="", description="Default SSH password for router connections (set via ROUTER_SSH_PASSWORD env var)")
    
    # CSI Processing settings
    csi_sampling_rate: int = Field(default=1000, description="CSI sampling rate")
    csi_window_size: int = Field(default=512, description="CSI window size")
    csi_overlap: float = Field(default=0.5, description="CSI window overlap")
    csi_noise_threshold: float = Field(default=0.1, description="CSI noise threshold")
    csi_human_detection_threshold: float = Field(default=0.8, description="CSI human detection threshold")
    csi_smoothing_factor: float = Field(default=0.9, description="CSI smoothing factor")
    csi_max_history_size: int = Field(default=500, description="CSI max history size")
    
    # Pose estimation settings
    pose_model_path: Optional[str] = Field(default=None, description="Path to pose estimation model")
    pose_confidence_threshold: float = Field(default=0.5, description="Minimum confidence threshold")
    pose_processing_batch_size: int = Field(default=32, description="Batch size for pose processing")
    pose_max_persons: int = Field(default=10, description="Maximum persons to detect per frame")
    
    # Streaming settings
    stream_fps: int = Field(default=30, description="Streaming frames per second")
    stream_buffer_size: int = Field(default=100, description="Stream buffer size")
    websocket_ping_interval: int = Field(default=60, description="WebSocket ping interval in seconds")
    websocket_timeout: int = Field(default=300, description="WebSocket timeout in seconds")
    
    # Logging settings
    log_level: str = Field(default="INFO", description="Logging level")
    log_format: str = Field(
        default="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
        description="Log format"
    )
    log_file: Optional[str] = Field(default=None, description="Log file path")
    log_directory: str = Field(default="./logs", description="Log directory path")
    log_max_size: int = Field(default=10485760, description="Max log file size in bytes (10MB)")
    log_backup_count: int = Field(default=5, description="Number of log backup files")
    
    # Monitoring settings
    metrics_enabled: bool = Field(default=True, description="Enable metrics collection")
    health_check_interval: int = Field(default=30, description="Health check interval in seconds")
    performance_monitoring: bool = Field(default=True, description="Enable performance monitoring")
    monitoring_interval_seconds: int = Field(default=60, description="Monitoring task interval in seconds")
    cleanup_interval_seconds: int = Field(default=3600, description="Cleanup task interval in seconds")
    backup_interval_seconds: int = Field(default=86400, description="Backup task interval in seconds")
    
    # Storage settings
    data_storage_path: str = Field(default="./data", description="Data storage directory")
    model_storage_path: str = Field(default="./models", description="Model storage directory")
    temp_storage_path: str = Field(default="./temp", description="Temporary storage directory")
    backup_directory: str = Field(default="./backups", description="Backup storage directory")
    max_storage_size_gb: int = Field(default=100, description="Maximum storage size in GB")
    
    # API settings
    api_prefix: str = Field(default="/api/v1", description="API prefix")
    docs_url: str = Field(default="/docs", description="API documentation URL")
    redoc_url: str = Field(default="/redoc", description="ReDoc documentation URL")
    openapi_url: str = Field(default="/openapi.json", description="OpenAPI schema URL")
    
    # Feature flags
    enable_authentication: bool = Field(default=True, description="Enable authentication")
    enable_rate_limiting: bool = Field(default=True, description="Enable rate limiting")
    enable_websockets: bool = Field(default=True, description="Enable WebSocket support")
    enable_historical_data: bool = Field(default=True, description="Enable historical data storage")
    enable_real_time_processing: bool = Field(default=True, description="Enable real-time processing")
    cors_enabled: bool = Field(default=True, description="Enable CORS middleware")
    cors_allow_credentials: bool = Field(default=True, description="Allow credentials in CORS")
    
    # Development settings
    mock_hardware: bool = Field(default=False, description="Use mock hardware for development")
    mock_pose_data: bool = Field(default=False, description="Use mock pose data for development")
    enable_test_endpoints: bool = Field(default=False, description="Enable test endpoints")
    
    # Cleanup settings
    csi_data_retention_days: int = Field(default=30, description="CSI data retention in days")
    pose_detection_retention_days: int = Field(default=30, description="Pose detection retention in days")
    metrics_retention_days: int = Field(default=7, description="Metrics retention in days")
    audit_log_retention_days: int = Field(default=90, description="Audit log retention in days")
    orphaned_session_threshold_days: int = Field(default=7, description="Orphaned session threshold in days")
    cleanup_batch_size: int = Field(default=1000, description="Cleanup batch size")
    
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=False
    )
    
    @field_validator("environment")
    @classmethod
    def validate_environment(cls, v):
        """Validate environment setting."""
        allowed_environments = ["development", "staging", "production"]
        if v not in allowed_environments:
            raise ValueError(f"Environment must be one of: {allowed_environments}")
        return v
    
    @field_validator("log_level")
    @classmethod
    def validate_log_level(cls, v):
        """Validate log level setting."""
        allowed_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
        if v.upper() not in allowed_levels:
            raise ValueError(f"Log level must be one of: {allowed_levels}")
        return v.upper()
    
    @field_validator("pose_confidence_threshold")
    @classmethod
    def validate_confidence_threshold(cls, v):
        """Validate confidence threshold."""
        if not 0.0 <= v <= 1.0:
            raise ValueError("Confidence threshold must be between 0.0 and 1.0")
        return v
    
    @field_validator("stream_fps")
    @classmethod
    def validate_stream_fps(cls, v):
        """Validate streaming FPS."""
        if not 1 <= v <= 60:
            raise ValueError("Stream FPS must be between 1 and 60")
        return v
    
    @field_validator("port")
    @classmethod
    def validate_port(cls, v):
        """Validate port number."""
        if not 1 <= v <= 65535:
            raise ValueError("Port must be between 1 and 65535")
        return v
    
    @field_validator("workers")
    @classmethod
    def validate_workers(cls, v):
        """Validate worker count."""
        if v < 1:
            raise ValueError("Workers must be at least 1")
        return v
    
    @field_validator("db_port")
    @classmethod
    def validate_db_port(cls, v):
        """Validate database port."""
        if not 1 <= v <= 65535:
            raise ValueError("Database port must be between 1 and 65535")
        return v
    
    @field_validator("redis_port")
    @classmethod
    def validate_redis_port(cls, v):
        """Validate Redis port."""
        if not 1 <= v <= 65535:
            raise ValueError("Redis port must be between 1 and 65535")
        return v
    
    @field_validator("db_pool_size")
    @classmethod
    def validate_db_pool_size(cls, v):
        """Validate database pool size."""
        if v < 1:
            raise ValueError("Database pool size must be at least 1")
        return v
    
    @field_validator("monitoring_interval_seconds", "cleanup_interval_seconds", "backup_interval_seconds")
    @classmethod
    def validate_interval_seconds(cls, v):
        """Validate interval settings."""
        if v < 0:
            raise ValueError("Interval seconds must be non-negative")
        return v
    @property
    def is_development(self) -> bool:
        """Check if running in development environment."""
        return self.environment == "development"
    
    @property
    def is_production(self) -> bool:
        """Check if running in production environment."""
        return self.environment == "production"
    
    @property
    def is_testing(self) -> bool:
        """Check if running in testing environment."""
        return self.environment == "testing"
    
    def get_database_url(self) -> str:
        """Get database URL with fallback."""
        if self.database_url:
            return self.database_url
        
        # Build URL from individual components if available
        if self.db_host and self.db_name and self.db_user:
            password_part = f":{self.db_password}" if self.db_password else ""
            return f"postgresql://{self.db_user}{password_part}@{self.db_host}:{self.db_port}/{self.db_name}"
        
        # Default SQLite database for development
        if self.is_development:
            return f"sqlite:///{self.data_storage_path}/wifi_densepose.db"
        
        # SQLite failsafe for production if enabled
        if self.enable_database_failsafe:
            return f"sqlite:///{self.sqlite_fallback_path}"
        
        raise ValueError("Database URL must be configured for non-development environments")
    
    def get_sqlite_fallback_url(self) -> str:
        """Get SQLite fallback database URL."""
        return f"sqlite:///{self.sqlite_fallback_path}"
    
    def get_redis_url(self) -> Optional[str]:
        """Get Redis URL with fallback."""
        if not self.redis_enabled:
            return None
            
        if self.redis_url:
            return self.redis_url
        
        # Build URL from individual components
        password_part = f":{self.redis_password}@" if self.redis_password else ""
        return f"redis://{password_part}{self.redis_host}:{self.redis_port}/{self.redis_db}"
    
    def get_cors_config(self) -> Dict[str, Any]:
        """Get CORS configuration."""
        if self.is_development:
            return {
                "allow_origins": ["*"],
                "allow_credentials": True,
                "allow_methods": ["*"],
                "allow_headers": ["*"],
            }
        
        return {
            "allow_origins": self.cors_origins,
            "allow_credentials": True,
            "allow_methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
            "allow_headers": ["Authorization", "Content-Type"],
        }
    
    def get_logging_config(self) -> Dict[str, Any]:
        """Get logging configuration."""
        config = {
            "version": 1,
            "disable_existing_loggers": False,
            "formatters": {
                "default": {
                    "format": self.log_format,
                },
                "detailed": {
                    "format": "%(asctime)s - %(name)s - %(levelname)s - %(module)s:%(lineno)d - %(message)s",
                },
            },
            "handlers": {
                "console": {
                    "class": "logging.StreamHandler",
                    "level": self.log_level,
                    "formatter": "default",
                    "stream": "ext://sys.stdout",
                },
            },
            "loggers": {
                "": {
                    "level": self.log_level,
                    "handlers": ["console"],
                },
                "uvicorn": {
                    "level": "INFO",
                    "handlers": ["console"],
                    "propagate": False,
                },
                "fastapi": {
                    "level": "INFO",
                    "handlers": ["console"],
                    "propagate": False,
                },
            },
        }
        
        # Add file handler if log file is specified
        if self.log_file:
            config["handlers"]["file"] = {
                "class": "logging.handlers.RotatingFileHandler",
                "level": self.log_level,
                "formatter": "detailed",
                "filename": self.log_file,
                "maxBytes": self.log_max_size,
                "backupCount": self.log_backup_count,
            }
            
            # Add file handler to all loggers
            for logger_config in config["loggers"].values():
                logger_config["handlers"].append("file")
        
        return config
    
    def create_directories(self):
        """Create necessary directories."""
        directories = [
            self.data_storage_path,
            self.model_storage_path,
            self.temp_storage_path,
            self.log_directory,
            self.backup_directory,
        ]
        
        for directory in directories:
            os.makedirs(directory, exist_ok=True)


@lru_cache()
def get_settings() -> Settings:
    """Get cached settings instance."""
    settings = Settings()
    settings.create_directories()
    return settings


def get_test_settings() -> Settings:
    """Get settings for testing."""
    return Settings(
        environment="testing",
        debug=True,
        secret_key="test-secret-key",
        database_url="sqlite:///:memory:",
        mock_hardware=True,
        mock_pose_data=True,
        enable_test_endpoints=True,
        log_level="DEBUG"
    )


def load_settings_from_file(file_path: str) -> Settings:
    """Load settings from a specific file."""
    return Settings(_env_file=file_path)


def validate_settings(settings: Settings) -> List[str]:
    """Validate settings and return list of issues."""
    issues = []
    
    # Check required settings for production
    if settings.is_production:
        if not settings.secret_key or settings.secret_key == "change-me":
            issues.append("Secret key must be set for production")
        
        if not settings.database_url and not (settings.db_host and settings.db_name and settings.db_user):
            issues.append("Database URL or database connection parameters must be set for production")
        
        if settings.debug:
            issues.append("Debug mode should be disabled in production")
        
        if "*" in settings.allowed_hosts:
            issues.append("Allowed hosts should be restricted in production")
        
        if "*" in settings.cors_origins:
            issues.append("CORS origins should be restricted in production")
    
    # Check storage paths exist
    try:
        settings.create_directories()
    except Exception as e:
        issues.append(f"Cannot create storage directories: {e}")
    
    return issues