"""Configuration management for CAFE."""

from pathlib import Path
from typing import Any, Dict, Optional
import yaml

from cafe.core.types import AgentCLI


class ConfigError(Exception):
    """Configuration error."""

    pass


def get_global_cafe_dir() -> Path:
    """Get global CAFE directory path.
    
    Returns:
        Path to ~/.cafe directory (creates if not exists)
    """
    global_dir = Path.home() / ".cafe"
    global_dir.mkdir(parents=True, exist_ok=True)
    return global_dir


def ensure_global_directories() -> None:
    """Ensure global CAFE directories exist.

    Creates the following directory structure:
    ~/.cafe/
    ├── agents/
    │   ├── pm/
    │   ├── developer/
    │   └── reviewer/
    └── templates/
        ├── plan/
        └── spec/
    """
    global_dir = get_global_cafe_dir()

    # Create agent subdirectories
    for role in ["pm", "developer", "reviewer"]:
        (global_dir / "agents" / role).mkdir(parents=True, exist_ok=True)

    # Create template subdirectories
    for template_type in ["plan", "spec"]:
        (global_dir / "templates" / template_type).mkdir(parents=True, exist_ok=True)


def resolve_sync_github_config(
    cli_value: Optional[bool],
    config_value: Optional[bool],
    has_issue_id: bool
) -> bool:
    """Resolve sync_github configuration value with proper priority.

    Priority order:
    1. CLI-provided value (--sync-github/--no-sync-github)
    2. Config file value (from issue.yaml)
    3. Default based on issue_id presence (True if issue_id exists, False otherwise)

    Args:
        cli_value: Value from CLI flag (None if not provided)
        config_value: Value from config file (None if not in config)
        has_issue_id: Whether issue_id is present in config

    Returns:
        Resolved boolean value for sync_github
    """
    # Priority: CLI value > config value > default based on issue_id
    if cli_value is not None:
        # CLI value takes precedence
        return cli_value
    elif config_value is not None:
        # Use value from config
        return config_value
    elif has_issue_id:
        # Default to True if issue_id is present (backward compatibility)
        return True
    else:
        # Default to False if no issue_id
        return False


class ConfigManager:
    """Manages CAFE configuration."""

    def __init__(self, config_dir: str = ".cafe") -> None:
        """Initialize config manager.

        Args:
            config_dir: Directory for configuration files
        """
        self.config_dir = Path(config_dir)
        self.config_dir.mkdir(parents=True, exist_ok=True)
        self.config_file = self.config_dir / "config.yaml"
        self._config: Optional[Dict[str, Any]] = None

    def load_config(self) -> Dict[str, Any]:
        """Load configuration from file.

        Returns:
            Configuration dictionary

        Raises:
            ConfigError: If config file is invalid or doesn't exist
        """
        if not self.config_file.exists():
            raise ConfigError(
                f"Configuration file not found: {self.config_file}\n"
                "Please run 'cafe init' first to initialize CAFE."
            )

        try:
            with open(self.config_file, "r") as f:
                self._config = yaml.safe_load(f)
                return self._config
        except yaml.YAMLError as e:
            raise ConfigError(f"Failed to load config: {e}") from e

    def get_default_config(self) -> Dict[str, Any]:
        """Get default configuration.

        Returns:
            Default configuration dictionary
        """
        return {
            "agents": {
                "pm": {
                    "name": "Roger",
                    "cli": "gemini",
                },
                "developer": {
                    "name": "David",
                    "cli": "claude",
                },
                "reviewer": {
                    "name": "Richard",
                    "cli": "gemini",
                },
            },
            "auto": {
                "max_review_iterations": 5,
            },
            "python_bin": "python3",
        }

    def save_config(self, config: Dict[str, Any]) -> None:
        """Save configuration to file.

        Args:
            config: Configuration to save
        """
        with open(self.config_file, "w") as f:
            yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
        self._config = config

    def validate_config(self, config: Dict[str, Any]) -> bool:
        """Validate configuration.

        Args:
            config: Configuration to validate

        Returns:
            True if valid

        Raises:
            ConfigError: If configuration is invalid
        """
        # Check required fields
        required_fields = ["agents"]
        for field in required_fields:
            if field not in config:
                raise ConfigError(f"Missing required field: {field}")

        # Validate agents
        if isinstance(config["agents"], list):
            for agent in config["agents"]:
                if "cli" in agent:
                    try:
                        AgentCLI(agent["cli"])
                    except ValueError:
                        raise ConfigError(
                            f"Invalid agent CLI: {agent['cli']}. "
                            f"Must be one of: {[t.value for t in AgentCLI]}"
                        )

        return True

    def get(self, key: str, default: Any = None) -> Any:
        """Get configuration value.

        Args:
            key: Configuration key (supports dot notation for nested keys)
            default: Default value if key not found

        Returns:
            Configuration value
        """
        if self._config is None:
            self.load_config()

        # Handle nested keys with dot notation
        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 configuration value.

        Args:
            key: Configuration key (supports dot notation for nested keys)
            value: Value to set

        Note:
            Supports aliases for agent CLI shortcuts:
            - 'pm' → 'agents.pm.cli'
            - 'dev' or 'developer' → 'agents.developer.cli'
            - 'reviewer' → 'agents.reviewer.cli'
        """
        if self._config is None:
            self.load_config()

        # Apply alias logic for agent CLI shortcuts
        key = self._resolve_alias(key)

        # Handle nested keys with dot notation
        keys = key.split(".")
        config = self._config

        # Navigate to the parent of the target key
        for k in keys[:-1]:
            if k not in config:
                config[k] = {}
            config = config[k]

        # Set the value
        config[keys[-1]] = value

        # Save to file
        self.save_config(self._config)

    def _resolve_alias(self, key: str) -> str:
        """Resolve key aliases for convenience.

        Args:
            key: Original key

        Returns:
            Resolved key

        Examples:
            'pm' → 'agents.pm.cli'
            'dev' → 'agents.developer.cli'
            'developer' → 'agents.developer.cli'
            'pm.name' → 'agents.pm.name' (no change needed, already has agent prefix)
        """
        # Agent CLI shortcuts: pm, dev/developer, reviewer (without dots)
        if key == 'pm':
            return 'agents.pm.cli'
        if key in ['dev', 'developer']:
            return 'agents.developer.cli'
        if key == 'reviewer':
            return 'agents.reviewer.cli'

        # If it starts with agent name but not agents., add agents prefix
        # e.g., 'pm.cli' → 'agents.pm.cli', 'pm.name' → 'agents.pm.name'
        if key.startswith('pm.'):
            return f'agents.{key}'
        if key.startswith('dev.') or key.startswith('developer.'):
            # Map both 'dev.' and 'developer.' to 'agents.developer.'
            suffix = key.split('.', 1)[1]
            return f'agents.developer.{suffix}'
        if key.startswith('reviewer.'):
            return f'agents.{key}'

        return key

    def reset(self) -> None:
        """Reset configuration to default values."""
        self._config = self.get_default_config()
        self.save_config(self._config)

    def merge_config(self, base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
        """Merge two configuration dictionaries.

        Args:
            base: Base configuration
            override: Configuration to override base

        Returns:
            Merged configuration (does not modify inputs)
        """
        result = base.copy()

        for key, value in override.items():
            if key in result and isinstance(result[key], dict) and isinstance(value, dict):
                # Recursively merge nested dictionaries
                result[key] = self.merge_config(result[key], value)
            else:
                result[key] = value

        return result
