"""
Configuration management utilities for the karaoke downloader.
Provides centralized configuration loading, validation, and management.
"""

import json
import platform
import sys
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, Optional, Union

# Default configuration values
DEFAULT_CONFIG = {
    "download_settings": {
        "format": "best[height<=720][ext=mp4]/best[height<=720]/best[ext=mp4]/best",
        "preferred_resolution": "720p",
        "audio_format": "mp3",
        "audio_quality": "0",
        "subtitle_language": "en",
        "subtitle_format": "srt",
        "write_metadata": False,
        "write_thumbnail": False,
        "write_description": False,
        "write_annotations": False,
        "write_comments": False,
        "write_subtitles": False,
        "embed_metadata": False,
        "add_metadata": False,
        "continue_downloads": True,
        "no_overwrites": True,
        "ignore_errors": True,
        "no_warnings": False,
    },
    "folder_structure": {
        "downloads_dir": "downloads",
        "logs_dir": "logs",
        "tracking_file": "data/karaoke_tracking.json",
    },
    "logging": {
        "level": "INFO",
        "format": "%(asctime)s - %(levelname)s - %(message)s",
        "include_console": True,
        "include_file": True,
    },
    "platform_settings": {
        "auto_detect_platform": True,
        "yt_dlp_paths": {
            "windows": "downloader/yt-dlp.exe",
            "macos": "downloader/yt-dlp_macos"
        }
    },
    "yt_dlp_path": "downloader/yt-dlp.exe",
}

# Resolution mapping for CLI arguments
RESOLUTION_MAP = {
    "480p": "480",
    "720p": "720",
    "1080p": "1080",
    "1440p": "1440",
    "2160p": "2160",
}


def detect_platform() -> str:
    """Detect the current platform and return platform name."""
    system = platform.system().lower()
    if system == "windows":
        return "windows"
    elif system == "darwin":
        return "macos"
    else:
        return "windows"  # Default to Windows for other platforms


def get_platform_yt_dlp_path(platform_paths: Dict[str, str]) -> str:
    """Get the appropriate yt-dlp path for the current platform."""
    platform_name = detect_platform()
    return platform_paths.get(platform_name, platform_paths.get("windows", "downloader/yt-dlp.exe"))


@dataclass
class DownloadSettings:
    """Configuration for download settings."""

    format: str = "best[height<=720][ext=mp4]/best[height<=720]/best[ext=mp4]/best"
    outtmpl: str = "%(title)s_720p.%(ext)s"
    merge_output_format: str = "mp4"
    noplaylist: bool = True
    postprocessors: list = None
    preferred_resolution: str = "720p"
    audio_format: str = "mp3"
    audio_quality: str = "0"
    subtitle_language: str = "en"
    subtitle_format: str = "srt"
    write_metadata: bool = False
    write_thumbnail: bool = False
    write_description: bool = False
    writedescription: bool = False
    write_annotations: bool = False
    writeannotations: bool = False
    write_comments: bool = False
    writecomments: bool = False
    write_subtitles: bool = False
    writesubtitles: bool = False
    writeinfojson: bool = False
    writethumbnail: bool = False
    embed_metadata: bool = False
    add_metadata: bool = False
    continue_downloads: bool = True
    continuedl: bool = True
    no_overwrites: bool = True
    nooverwrites: bool = True
    ignore_errors: bool = True
    ignoreerrors: bool = True
    no_warnings: bool = False

    def __post_init__(self):
        """Initialize default values for complex fields."""
        if self.postprocessors is None:
            self.postprocessors = [
                {
                    "key": "FFmpegExtractAudio",
                    "preferredcodec": "mp3",
                    "preferredquality": "0",
                }
            ]


@dataclass
class FolderStructure:
    """Configuration for folder structure."""

    downloads_dir: str = "downloads"
    logs_dir: str = "logs"
    tracking_file: str = "data/karaoke_tracking.json"


@dataclass
class LoggingConfig:
    """Configuration for logging."""

    level: str = "INFO"
    format: str = "%(asctime)s - %(levelname)s - %(message)s"
    include_console: bool = True
    include_file: bool = True


@dataclass
class AppConfig:
    """Main application configuration."""

    download_settings: DownloadSettings = field(default_factory=DownloadSettings)
    folder_structure: FolderStructure = field(default_factory=FolderStructure)
    logging: LoggingConfig = field(default_factory=LoggingConfig)
    yt_dlp_path: str = "downloader/yt-dlp.exe"
    _config_file: Optional[Path] = None
    _last_modified: Optional[datetime] = None


class ConfigManager:
    """
    Manages application configuration with loading, validation, and caching.
    """

    def __init__(self, config_file: Union[str, Path] = "data/config.json"):
        """
        Initialize the configuration manager.

        Args:
            config_file: Path to the configuration file
        """
        self.config_file = Path(config_file)
        self._config: Optional[AppConfig] = None
        self._last_modified: Optional[datetime] = None

    def load_config(self, force_reload: bool = False) -> AppConfig:
        """
        Load configuration from file with caching.

        Args:
            force_reload: Force reload even if file hasn't changed

        Returns:
            AppConfig instance
        """
        # Check if we need to reload
        if not force_reload and self._config is not None:
            if self.config_file.exists():
                current_mtime = datetime.fromtimestamp(self.config_file.stat().st_mtime)
                if self._last_modified and current_mtime <= self._last_modified:
                    return self._config

        # Load configuration
        config_data = self._load_config_file()
        self._config = self._create_config_from_dict(config_data)
        self._last_modified = datetime.now()

        return self._config

    def _load_config_file(self) -> Dict[str, Any]:
        """
        Load configuration from file with fallback to defaults.

        Returns:
            Configuration dictionary
        """
        if self.config_file.exists():
            try:
                with open(self.config_file, "r", encoding="utf-8") as f:
                    file_config = json.load(f)
                # Merge with defaults
                return self._merge_configs(DEFAULT_CONFIG, file_config)
            except (json.JSONDecodeError, FileNotFoundError) as e:
                print(f"Warning: Could not load config.json: {e}")
                print("Using default configuration.")

        return DEFAULT_CONFIG.copy()

    def _merge_configs(
        self, default: Dict[str, Any], user: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        Merge user configuration with defaults.

        Args:
            default: Default configuration
            user: User configuration

        Returns:
            Merged configuration
        """
        merged = default.copy()

        for key, value in user.items():
            if (
                key in merged
                and isinstance(merged[key], dict)
                and isinstance(value, dict)
            ):
                merged[key] = self._merge_configs(merged[key], value)
            else:
                merged[key] = value

        return merged

    def _create_config_from_dict(self, config_data: Dict[str, Any]) -> AppConfig:
        """
        Create AppConfig from dictionary.

        Args:
            config_data: Configuration dictionary

        Returns:
            AppConfig instance
        """
        download_settings = DownloadSettings(**config_data.get("download_settings", {}))
        folder_structure = FolderStructure(**config_data.get("folder_structure", {}))
        logging_config = LoggingConfig(**config_data.get("logging", {}))

        # Handle platform-specific yt-dlp path
        yt_dlp_path = config_data.get("yt_dlp_path", "downloader/yt-dlp.exe")
        
        # Check if platform auto-detection is enabled
        platform_settings = config_data.get("platform_settings", {})
        if platform_settings.get("auto_detect_platform", True):
            platform_paths = platform_settings.get("yt_dlp_paths", {})
            if platform_paths:
                yt_dlp_path = get_platform_yt_dlp_path(platform_paths)

        return AppConfig(
            download_settings=download_settings,
            folder_structure=folder_structure,
            logging=logging_config,
            yt_dlp_path=yt_dlp_path,
            _config_file=self.config_file,
        )

    def update_resolution(self, resolution: str) -> None:
        """
        Update the download format based on resolution.

        Args:
            resolution: Resolution string (e.g., "720p", "1080p")
        """
        if self._config is None:
            self.load_config()

        if resolution in RESOLUTION_MAP:
            height = RESOLUTION_MAP[resolution]
            format_str = f"best[height<={height}][ext=mp4]/best[height<={height}]/best[ext=mp4]/best"
            self._config.download_settings.format = format_str
            self._config.download_settings.preferred_resolution = resolution
            print(f"🎬 Using resolution: {resolution}")

    def get_config(self) -> AppConfig:
        """
        Get the current configuration.

        Returns:
            AppConfig instance
        """
        if self._config is None:
            return self.load_config()
        return self._config

    def save_config(self) -> None:
        """
        Save current configuration to file.
        """
        if self._config is None:
            return

        config_dict = {
            "download_settings": self._config.download_settings.__dict__,
            "folder_structure": self._config.folder_structure.__dict__,
            "logging": self._config.logging.__dict__,
            "yt_dlp_path": self._config.yt_dlp_path,
        }

        # Ensure directory exists
        self.config_file.parent.mkdir(parents=True, exist_ok=True)

        with open(self.config_file, "w", encoding="utf-8") as f:
            json.dump(config_dict, f, indent=2, ensure_ascii=False)

        print(f"Configuration saved to {self.config_file}")


# Global configuration manager instance
_config_manager: Optional[ConfigManager] = None


def get_config_manager() -> ConfigManager:
    """
    Get the global configuration manager instance.

    Returns:
        ConfigManager instance
    """
    global _config_manager
    if _config_manager is None:
        _config_manager = ConfigManager()
    return _config_manager


def load_config(force_reload: bool = False) -> AppConfig:
    """
    Load configuration using the global manager.

    Args:
        force_reload: Force reload even if file hasn't changed

    Returns:
        AppConfig instance
    """
    return get_config_manager().load_config(force_reload)
