#!/usr/bin/env python3
"""
Configuration management for the build system.
Loads default configuration from config.json and provides
access to build settings, paths, and compiler flags.
"""

import os
import json
import subprocess
from pathlib import Path
from typing import Dict, List, Any, Optional

from importlib.resources import files


class Config:
    """Central configuration manager for the build system."""
    
    def __init__(self, config_override: Optional[Dict[str, Any]] = None):
        """
        Initialize configuration.
        
        Args:
            config_override: Optional dictionary to override default config values
        """
        self._config = self._load_default_config()
        if config_override:
            self._deep_update(self._config, config_override)
        
        # Cache for computed values
        self._git_hash: Optional[str] = None
        self._python_cmd: Optional[str] = None
    
    def _load_default_config(self) -> Dict[str, Any]:
        """Load configuration from config.json."""
        try:
            # Load config.json from the same package
            config_file = files(__package__).joinpath('config.json') # type: ignore
            with config_file.open('r') as f:
                return json.load(f)
        except Exception as e:
            print(f"Warning: Could not load config.json: {e}")
            print("Using minimal default configuration")
            return self._minimal_default_config()
    
    def _minimal_default_config(self) -> Dict[str, Any]:
        """Fallback configuration if config.json cannot be loaded."""
        return {
            "directories": {
                "src": "src",
                "test": "tests",
                "obj": "obj",
                "bin": "bin",
                "include": "include"
            },
            "targets": {},
            "compiler_flags": {},
            "test_generation": {},
            "python_command": {"windows": "python", "unix": "python3"}
        }
    
    def _deep_update(self, base: Dict, update: Dict) -> None:
        """Recursively update nested dictionaries."""
        for key, value in update.items():
            if isinstance(value, dict) and key in base and isinstance(base[key], dict):
                self._deep_update(base[key], value)
            else:
                base[key] = value
    
    # -------------------------------------------------------------------------
    # Directory paths
    # -------------------------------------------------------------------------
    
    @property
    def src_dir(self) -> Path:
        """Source directory path."""
        return Path(self._config["directories"]["src"])
    
    @property
    def test_dir(self) -> Path:
        """Test directory path."""
        return Path(self._config["directories"]["test"])
    
    @property
    def obj_dir(self) -> Path:
        """Object files directory path."""
        return Path(self._config["directories"]["obj"])
    
    @property
    def bin_dir(self) -> Path:
        """Binary output directory path."""
        return Path(self._config["directories"]["bin"])
    
    @property
    def include_dir(self) -> Path:
        """Include directory path."""
        return Path(self._config["directories"]["include"])
    
    # -------------------------------------------------------------------------
    # Target configuration
    # -------------------------------------------------------------------------
    
    def get_target_config(self, target_name: str) -> Dict[str, Any]:
        """
        Get configuration for a specific target.
        
        Args:
            target_name: Name of the target (e.g., 'linux', 'windows', 'rp2040')
        
        Returns:
            Dictionary containing target-specific configuration
        """
        return self._config.get("targets", {}).get(target_name, {})
    
    def get_binary_name(self, target_name: str) -> str:
        """Get the output binary name for a target."""
        target_config = self.get_target_config(target_name)
        return target_config.get("binary_name", "sls")
    
    def get_test_binary_name(self, target_name: str) -> str:
        """Get the test binary name for a target."""
        target_config = self.get_target_config(target_name)
        return target_config.get("test_binary_name", "sls_tests")
    
    # -------------------------------------------------------------------------
    # Compiler flags
    # -------------------------------------------------------------------------
    
    def get_compiler_flags(self, compiler: str, flag_type: str = "common") -> List[str]:
        """
        Get compiler flags for a specific compiler and flag type.
        
        Args:
            compiler: Compiler name (e.g., 'gcc', 'msvc', 'arm_gcc')
            flag_type: Type of flags ('common', 'test', 'link')
        
        Returns:
            List of compiler flags
        """
        compiler_config = self._config.get("compiler_flags", {}).get(compiler, {})
        return compiler_config.get(flag_type, []).copy()
    
    # -------------------------------------------------------------------------
    # Test generation
    # -------------------------------------------------------------------------
    
    @property
    def test_script_path(self) -> Path:
        """Path to the test generation script."""
        path_str = self._config.get("test_generation", {}).get("script_path", "")
        return Path(path_str) if path_str else Path()
    
    @property
    def test_yaml_path(self) -> Path:
        """Path to the test cases YAML file."""
        path_str = self._config.get("test_generation", {}).get("yaml_path", "")
        return Path(path_str) if path_str else Path()
    
    @property
    def test_output_file(self) -> Path:
        """Path to the generated test output file."""
        path_str = self._config.get("test_generation", {}).get("output_file", "")
        return Path(path_str) if path_str else Path()
    
    # -------------------------------------------------------------------------
    # Git information
    # -------------------------------------------------------------------------
    
    @property
    def git_hash(self) -> str:
        """Get the current git commit hash with date."""
        if self._git_hash is None:
            self._git_hash = self._compute_git_hash()
        return self._git_hash
    
    def _compute_git_hash(self) -> str:
        """Compute git commit hash and date."""
        try:
            result_hash = subprocess.check_output(
                ["git", "describe", "--always", "--dirty", "--abbrev=7"],
                cwd=".",
                stderr=subprocess.DEVNULL,
                text=True
            ).strip()
            result_date = subprocess.check_output(
                ["git", "show", "-s", "--format=%ci"],
                cwd=".",
                stderr=subprocess.DEVNULL,
                text=True
            ).strip()
            return f"{result_hash} {result_date}"
        except Exception:
            return "unknown"
    
    # -------------------------------------------------------------------------
    # Platform-specific settings
    # -------------------------------------------------------------------------
    
    @property
    def python_command(self) -> str:
        """Get the appropriate Python command for the current platform."""
        if self._python_cmd is None:
            if os.name == "nt":
                self._python_cmd = self._config.get("python_command", {}).get("windows", "python")
            else:
                self._python_cmd = self._config.get("python_command", {}).get("unix", "python3")
        return self._python_cmd # type: ignore
    
    # -------------------------------------------------------------------------
    # Environment variables
    # -------------------------------------------------------------------------
    
    def get_env_var(self, target: str, var_name: str, default: Optional[str] = None) -> Optional[str]:
        """
        Get an environment variable value, with target-specific defaults.
        
        Args:
            target: Target name
            var_name: Environment variable name
            default: Default value if not found
        
        Returns:
            Environment variable value or default
        """
        target_config = self.get_target_config(target)
        env_key = target_config.get(f"{var_name}_env")
        
        if env_key and env_key in os.environ:
            return os.environ[env_key]
        
        config_default = target_config.get(f"{var_name}_default")
        if config_default:
            # Expand ~ in paths
            return str(Path(config_default).expanduser())
        
        return default


# Global config instance
_config_instance: Optional[Config] = None


def get_config() -> Config:
    """Get the global configuration instance."""
    global _config_instance
    if _config_instance is None:
        _config_instance = Config()
    return _config_instance


def set_config(config: Config) -> None:
    """Set the global configuration instance."""
    global _config_instance
    _config_instance = config
