"""
Configuration management for SoraCLI.

Handles loading, saving, and validating configuration from YAML files.
"""

import json
import os
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Any, Dict, Optional

try:
    import yaml
    YAML_AVAILABLE = True
except ImportError:
    YAML_AVAILABLE = False


# Default configuration values
DEFAULT_CONFIG = {
    'location': 'London',
    'theme': 'default',
    'refresh_rate': 60,
    'sound': False,
    'units': 'metric',
    'api_key': None,
    'intensity': 1.0,
    'show_status': True,
}

# Config file locations
CONFIG_DIR = Path.home() / ".soracli"
CONFIG_FILE = CONFIG_DIR / "config.yaml"
THEMES_DIR = Path(__file__).parent / "themes"
USER_THEMES_DIR = CONFIG_DIR / "themes"


@dataclass
class SoraConfig:
    """Configuration container for SoraCLI."""
    
    location: str = 'London'
    theme: str = 'default'
    refresh_rate: int = 60
    sound: bool = False
    units: str = 'metric'
    api_key: Optional[str] = None
    intensity: float = 1.0
    show_status: bool = True
    
    def __post_init__(self):
        """Validate configuration after initialization."""
        self.validate()
    
    def validate(self) -> None:
        """Validate configuration values."""
        if self.refresh_rate < 1:
            self.refresh_rate = 1
        elif self.refresh_rate > 3600:
            self.refresh_rate = 3600
        
        if self.units not in ('metric', 'imperial', 'standard'):
            self.units = 'metric'
        
        if self.intensity < 0.1:
            self.intensity = 0.1
        elif self.intensity > 2.0:
            self.intensity = 2.0
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert configuration to dictionary."""
        return asdict(self)
    
    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'SoraConfig':
        """Create configuration from dictionary."""
        # Filter out unknown keys
        valid_keys = {f.name for f in cls.__dataclass_fields__.values()}
        filtered_data = {k: v for k, v in data.items() if k in valid_keys}
        return cls(**filtered_data)


def load_config() -> SoraConfig:
    """
    Load configuration from file or return defaults.
    
    Returns:
        SoraConfig object with loaded or default settings
    """
    if not CONFIG_FILE.exists():
        return SoraConfig()
    
    try:
        with open(CONFIG_FILE, 'r') as f:
            if YAML_AVAILABLE:
                data = yaml.safe_load(f) or {}
            else:
                # Fallback to simple parsing
                data = _parse_simple_yaml(f.read())
        
        # Merge with defaults
        config_data = {**DEFAULT_CONFIG, **data}
        return SoraConfig.from_dict(config_data)
    
    except Exception as e:
        print(f"Warning: Could not load config file: {e}")
        return SoraConfig()


def save_config(config: SoraConfig) -> None:
    """
    Save configuration to file.
    
    Args:
        config: SoraConfig object to save
    """
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    
    try:
        with open(CONFIG_FILE, 'w') as f:
            if YAML_AVAILABLE:
                yaml.safe_dump(config.to_dict(), f, default_flow_style=False)
            else:
                # Simple YAML-like output
                for key, value in config.to_dict().items():
                    if value is None:
                        f.write(f"{key}: null\n")
                    elif isinstance(value, bool):
                        f.write(f"{key}: {'true' if value else 'false'}\n")
                    elif isinstance(value, str):
                        f.write(f"{key}: {value}\n")
                    else:
                        f.write(f"{key}: {value}\n")
    except Exception as e:
        raise RuntimeError(f"Failed to save config: {e}")


def init_config(
    location: Optional[str] = None,
    theme: Optional[str] = None,
    refresh_rate: Optional[int] = None,
    sound: Optional[bool] = None,
    units: Optional[str] = None,
    api_key: Optional[str] = None,
) -> SoraConfig:
    """
    Initialize configuration with provided values or defaults.
    
    Args:
        location: Default location for weather
        theme: Theme name to use
        refresh_rate: Weather refresh rate in seconds
        sound: Enable sound effects
        units: Temperature units
        api_key: OpenWeatherMap API key
    
    Returns:
        Created SoraConfig object
    """
    config = SoraConfig(
        location=location or DEFAULT_CONFIG['location'],
        theme=theme or DEFAULT_CONFIG['theme'],
        refresh_rate=refresh_rate or DEFAULT_CONFIG['refresh_rate'],
        sound=sound if sound is not None else DEFAULT_CONFIG['sound'],
        units=units or DEFAULT_CONFIG['units'],
        api_key=api_key,
    )
    
    save_config(config)
    return config


def load_theme(theme_name: str) -> Dict[str, Any]:
    """
    Load a theme by name.
    
    Args:
        theme_name: Name of the theme to load
    
    Returns:
        Theme dictionary with color and style settings
    """
    # Check user themes first
    user_theme_file = USER_THEMES_DIR / f"{theme_name}.json"
    if user_theme_file.exists():
        try:
            with open(user_theme_file, 'r') as f:
                return json.load(f)
        except Exception:
            pass
    
    # Check built-in themes
    builtin_theme_file = THEMES_DIR / f"{theme_name}.json"
    if builtin_theme_file.exists():
        try:
            with open(builtin_theme_file, 'r') as f:
                return json.load(f)
        except Exception:
            pass
    
    # Return default theme
    return get_default_theme()


def get_default_theme() -> Dict[str, Any]:
    """Get the default theme settings."""
    return {
        "name": "default",
        "description": "Default SoraCLI theme",
        "foreground": "white",
        "background": None,
        "rain_colors": ["cyan", "blue", "white"],
        "snow_colors": ["white", "bright_white"],
        "lightning_color": "bright_yellow",
        "fog_colors": ["gray", "white"],
        "star_colors": ["white", "bright_white", "yellow"],
        "cloud_colors": ["white", "gray"],
        "status_fg": "black",
        "status_bg": "white",
    }


def list_themes() -> list:
    """
    List all available themes.
    
    Returns:
        List of theme names
    """
    themes = set()
    
    # Built-in themes
    if THEMES_DIR.exists():
        for file in THEMES_DIR.glob("*.json"):
            themes.add(file.stem)
    
    # User themes
    if USER_THEMES_DIR.exists():
        for file in USER_THEMES_DIR.glob("*.json"):
            themes.add(file.stem)
    
    # Always include default
    themes.add("default")
    
    return sorted(themes)


def _parse_simple_yaml(content: str) -> Dict[str, Any]:
    """Simple YAML parser for basic key: value format."""
    result = {}
    
    for line in content.strip().split('\n'):
        line = line.strip()
        if not line or line.startswith('#'):
            continue
        
        if ':' in line:
            key, value = line.split(':', 1)
            key = key.strip()
            value = value.strip()
            
            # Parse value type
            if value.lower() == 'true':
                result[key] = True
            elif value.lower() == 'false':
                result[key] = False
            elif value.lower() == 'null' or value == '':
                result[key] = None
            elif value.isdigit():
                result[key] = int(value)
            else:
                try:
                    result[key] = float(value)
                except ValueError:
                    result[key] = value
    
    return result


def get_api_key() -> Optional[str]:
    """
    Get API key from config or environment.
    
    Returns:
        API key string or None
    """
    # First check environment
    env_key = os.environ.get('OPENWEATHERMAP_API_KEY')
    if env_key:
        return env_key
    
    # Then check config
    config = load_config()
    return config.api_key


def ensure_config_dir() -> Path:
    """Ensure config directory exists and return path."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    USER_THEMES_DIR.mkdir(parents=True, exist_ok=True)
    return CONFIG_DIR
