"""
Configuration management for SBYB.

This module provides utilities for managing configuration settings across
the SBYB library components.
"""

from typing import Any, Dict, Optional

import yaml


class Config:
    """Configuration manager for SBYB components."""
    
    DEFAULT_CONFIG = {
        "preprocessing": {
            "missing_values": {
                "strategy": "auto",
                "numeric_strategy": "mean",
                "categorical_strategy": "mode",
                "max_missing_ratio": 0.8
            },
            "outliers": {
                "detection_method": "iqr",
                "threshold": 1.5,
                "handling_strategy": "clip"
            },
            "scaling": {
                "method": "standard",
                "with_mean": True,
                "with_std": True
            },
            "encoding": {
                "categorical_method": "auto",
                "text_method": "count",
                "max_categories": 20
            }
        },
        "task_detection": {
            "threshold": 0.7,
            "default_task": "classification"
        },
        "automl": {
            "time_limit": 300,
            "cv_folds": 5,
            "scoring": "auto",
            "n_trials": 50,
            "early_stopping": True,
            "models": ["all"],
            "stack_models": True
        },
        "evaluation": {
            "metrics": ["auto"],
            "cv_folds": 5,
            "generate_plots": True,
            "shap_samples": 100
        },
        "deployment": {
            "api_framework": "fastapi",
            "host": "0.0.0.0",
            "port": 8000,
            "ui_framework": "streamlit"
        },
        "ui": {
            "theme": "light",
            "page_title": "SBYB Model",
            "description": "Generated by SBYB"
        },
        "tracking": {
            "log_level": "info",
            "save_artifacts": True,
            "log_dir": "./sbyb_logs"
        }
    }
    
    def __init__(self, config: Optional[Dict[str, Any]] = None, config_path: Optional[str] = None):
        """
        Initialize the configuration.
        
        Args:
            config: Configuration dictionary to override defaults.
            config_path: Path to a YAML configuration file.
        """
        self.config = self.DEFAULT_CONFIG.copy()
        
        if config_path:
            self._load_from_file(config_path)
            
        if config:
            self._update_config(config)
    
    def _load_from_file(self, config_path: str) -> None:
        """
        Load configuration from a YAML file.
        
        Args:
            config_path: Path to the configuration file.
        """
        try:
            with open(config_path, 'r') as f:
                file_config = yaml.safe_load(f)
                if file_config:
                    self._update_config(file_config)
        except Exception as e:
            raise ValueError(f"Failed to load configuration from {config_path}: {str(e)}")
    
    def _update_config(self, config: Dict[str, Any]) -> None:
        """
        Update the configuration with new values.
        
        Args:
            config: Configuration dictionary with new values.
        """
        self._recursive_update(self.config, config)
    
    def _recursive_update(self, base_config: Dict[str, Any], new_config: Dict[str, Any]) -> None:
        """
        Recursively update a nested configuration dictionary.
        
        Args:
            base_config: Base configuration to update.
            new_config: New configuration values.
        """
        for key, value in new_config.items():
            if isinstance(value, dict) and key in base_config and isinstance(base_config[key], dict):
                self._recursive_update(base_config[key], value)
            else:
                base_config[key] = value
    
    def get(self, key: str, default: Any = None) -> Any:
        """
        Get a configuration value.
        
        Args:
            key: Configuration key in dot notation (e.g., 'preprocessing.scaling.method').
            default: Default value to return if the key is not found.
            
        Returns:
            Configuration value or default.
        """
        keys = key.split('.')
        value = self.config
        
        for k in keys:
            if isinstance(value, dict) and k in value:
                value = value[k]
            else:
                return default
                
        return value
    
    def set(self, key: str, value: Any) -> None:
        """
        Set a configuration value.
        
        Args:
            key: Configuration key in dot notation (e.g., 'preprocessing.scaling.method').
            value: Value to set.
        """
        keys = key.split('.')
        config = self.config
        
        for i, k in enumerate(keys[:-1]):
            if k not in config:
                config[k] = {}
            config = config[k]
            
        config[keys[-1]] = value
    
    def to_dict(self) -> Dict[str, Any]:
        """
        Convert the configuration to a dictionary.
        
        Returns:
            Configuration dictionary.
        """
        return self.config.copy()
    
    def save(self, config_path: str) -> None:
        """
        Save the configuration to a YAML file.
        
        Args:
            config_path: Path to save the configuration file.
        """
        try:
            with open(config_path, 'w') as f:
                yaml.dump(self.config, f, default_flow_style=False)
        except Exception as e:
            raise ValueError(f"Failed to save configuration to {config_path}: {str(e)}")
