#!/usr/bin/env python3
"""
Central configuration for Terminal Control MCP Server
All configuration options and environment variable handling
"""

import os
from dataclasses import dataclass, field

# TOML support with automatic fallback
try:
    import tomllib
except ImportError:
    import tomli as tomllib  # type: ignore[no-redef]
from enum import Enum
from pathlib import Path
from typing import Any, TypedDict


class SecurityLevel(Enum):
    """Security levels for the MCP server"""

    OFF = "off"  # No security validation (allows all commands)
    LOW = "low"  # Basic input validation only
    MEDIUM = "medium"  # Standard protection (blocks common dangerous commands)
    HIGH = "high"  # Full protection (current default behavior)


class TerminalEmulator(TypedDict):
    """Terminal emulator configuration"""

    name: str
    command: list[str]


@dataclass
class ServerConfig:
    """Central configuration for the MCP server"""

    # Web server configuration
    web_enabled: bool = False
    web_host: str = "0.0.0.0"
    web_port: int = 8080
    web_auto_port: bool = True  # Automatically select unique port to avoid conflicts
    external_web_host: str | None = None

    # Security configuration
    security_level: SecurityLevel = SecurityLevel.HIGH
    max_calls_per_minute: int = 60
    max_sessions: int = 50

    # Session configuration
    default_shell: str = "bash"
    session_timeout: int = 30

    # Logging configuration
    log_level: str = "INFO"

    # Terminal configuration
    terminal_width: int = 120
    terminal_height: int = 30
    terminal_close_timeout: float = 5.0
    terminal_process_check_timeout: float = 1.0
    terminal_polling_interval: float = 0.05
    terminal_send_input_delay: float = 0.1
    terminal_emulators: list[TerminalEmulator] = field(default_factory=lambda: [])

    # Note: agent_name is handled dynamically per connection, not in static config

    @classmethod
    def from_config_and_environment(
        cls, config_file: str | None = None
    ) -> "ServerConfig":
        """Create configuration from TOML config file and environment variables

        Environment variables take precedence over config file values.
        Config file is loaded from:
        1. Specified config_file path
        2. ./terminal-control.toml
        3. ~/.config/terminal-control.toml
        4. /etc/terminal-control.toml
        """
        config_data = cls._load_toml_config(config_file)

        # Start with class defaults - single source of truth
        defaults = cls()

        return cls(
            # Web server
            web_enabled=cls._get_bool_value(
                "TERMINAL_CONTROL_WEB_ENABLED",
                config_data.get("web", {}).get("enabled", defaults.web_enabled),
            ),
            web_host=cls._get_str_value(
                "TERMINAL_CONTROL_WEB_HOST",
                config_data.get("web", {}).get("host", defaults.web_host),
            )
            or defaults.web_host,
            web_port=cls._get_int_value(
                "TERMINAL_CONTROL_WEB_PORT",
                config_data.get("web", {}).get("port", defaults.web_port),
            ),
            web_auto_port=cls._get_bool_value(
                "TERMINAL_CONTROL_WEB_AUTO_PORT",
                config_data.get("web", {}).get("auto_port", defaults.web_auto_port),
            ),
            external_web_host=cls._get_str_value(
                "TERMINAL_CONTROL_EXTERNAL_HOST",
                config_data.get("web", {}).get(
                    "external_host", defaults.external_web_host
                ),
            ),
            # Security
            security_level=cls._get_security_level(
                "TERMINAL_CONTROL_SECURITY_LEVEL",
                config_data.get("security", {}).get(
                    "level", defaults.security_level.value
                ),
            ),
            max_calls_per_minute=cls._get_int_value(
                "TERMINAL_CONTROL_MAX_CALLS_PER_MINUTE",
                config_data.get("security", {}).get(
                    "max_calls_per_minute", defaults.max_calls_per_minute
                ),
            ),
            max_sessions=cls._get_int_value(
                "TERMINAL_CONTROL_MAX_SESSIONS",
                config_data.get("security", {}).get(
                    "max_sessions", defaults.max_sessions
                ),
            ),
            # Sessions
            default_shell=cls._get_str_value(
                "TERMINAL_CONTROL_DEFAULT_SHELL",
                config_data.get("session", {}).get(
                    "default_shell", defaults.default_shell
                ),
            )
            or defaults.default_shell,
            session_timeout=cls._get_int_value(
                "TERMINAL_CONTROL_SESSION_TIMEOUT",
                config_data.get("session", {}).get("timeout", defaults.session_timeout),
            ),
            # Logging
            log_level=cls._get_str_value(
                "TERMINAL_CONTROL_LOG_LEVEL",
                config_data.get("logging", {}).get("level", defaults.log_level),
            )
            or defaults.log_level,
            # Terminal
            terminal_width=cls._get_int_value(
                "TERMINAL_CONTROL_TERMINAL_WIDTH",
                config_data.get("terminal", {}).get("width", defaults.terminal_width),
            ),
            terminal_height=cls._get_int_value(
                "TERMINAL_CONTROL_TERMINAL_HEIGHT",
                config_data.get("terminal", {}).get("height", defaults.terminal_height),
            ),
            terminal_close_timeout=cls._get_float_value(
                "TERMINAL_CONTROL_TERMINAL_CLOSE_TIMEOUT",
                config_data.get("terminal", {}).get(
                    "close_timeout", defaults.terminal_close_timeout
                ),
            ),
            terminal_process_check_timeout=cls._get_float_value(
                "TERMINAL_CONTROL_TERMINAL_PROCESS_CHECK_TIMEOUT",
                config_data.get("terminal", {}).get(
                    "process_check_timeout", defaults.terminal_process_check_timeout
                ),
            ),
            terminal_polling_interval=cls._get_float_value(
                "TERMINAL_CONTROL_TERMINAL_POLLING_INTERVAL",
                config_data.get("terminal", {}).get(
                    "polling_interval", defaults.terminal_polling_interval
                ),
            ),
            terminal_send_input_delay=cls._get_float_value(
                "TERMINAL_CONTROL_TERMINAL_SEND_INPUT_DELAY",
                config_data.get("terminal", {}).get(
                    "send_input_delay", defaults.terminal_send_input_delay
                ),
            ),
            terminal_emulators=cls._get_terminal_emulators(
                config_data.get("terminal", {}).get("emulators", [])
            ),
        )

    @classmethod
    def from_environment(cls) -> "ServerConfig":
        """Create configuration from environment variables only (backwards compatibility)"""
        return cls.from_config_and_environment(None)

    @staticmethod
    def _load_toml_config(config_file: str | None) -> dict[str, Any]:
        """Load configuration from TOML file"""
        # If a specific config file is provided, only try that file
        if config_file:
            config_path = Path(config_file)
            if config_path.exists() and config_path.is_file():
                try:
                    with open(config_path, "rb") as f:
                        config_data = tomllib.load(f)
                    print(f"Loaded configuration from: {config_path}")
                    return config_data
                except Exception as e:
                    print(f"Warning: Could not read config file {config_path}: {e}")
            return {}  # Return empty dict if specific file not found/readable

        # If no specific file provided, search default locations
        default_locations = [
            Path("terminal-control.toml"),
            Path(os.path.expanduser("~/.config/terminal-control.toml")),
            Path("/etc/terminal-control.toml"),
        ]

        for config_path in default_locations:
            if config_path.exists() and config_path.is_file():
                try:
                    with open(config_path, "rb") as f:
                        config_data = tomllib.load(f)
                    print(f"Loaded configuration from: {config_path}")
                    return config_data
                except Exception as e:
                    print(f"Warning: Could not read config file {config_path}: {e}")

        return {}  # Return empty dict if no config file found

    @staticmethod
    def _get_str_value(env_var: str, default: str | None) -> str | None:
        """Get string value from environment, falling back to default"""
        env_value = os.environ.get(env_var)
        return env_value if env_value is not None else default

    @staticmethod
    def _get_int_value(env_var: str, default: int) -> int:
        """Get integer value from environment, falling back to default"""
        env_value = os.environ.get(env_var)
        if env_value is not None:
            try:
                return int(env_value)
            except ValueError:
                pass
        return default

    @staticmethod
    def _get_bool_value(env_var: str, default: bool) -> bool:
        """Get boolean value from environment, falling back to default"""
        env_value = os.environ.get(env_var)
        if env_value is not None:
            return env_value.lower() in ("true", "1", "yes", "on")
        return default

    @staticmethod
    def _get_float_value(env_var: str, default: float) -> float:
        """Get float value from environment, falling back to default"""
        env_value = os.environ.get(env_var)
        if env_value is not None:
            try:
                return float(env_value)
            except ValueError:
                pass
        return default

    @staticmethod
    def _get_security_level(env_var: str, default: str) -> SecurityLevel:
        """Get security level from environment, falling back to default"""
        env_value = os.environ.get(env_var)
        level_str = env_value if env_value is not None else default
        try:
            return SecurityLevel(level_str.lower())
        except ValueError:
            return SecurityLevel.HIGH

    @staticmethod
    def _get_terminal_emulators(
        emulator_configs: list[dict[str, Any]],
    ) -> list[TerminalEmulator]:
        """Parse terminal emulator configurations from TOML"""
        if not emulator_configs:
            # Default terminal emulators if none configured
            return [
                {"name": "gnome-terminal", "command": ["gnome-terminal", "--"]},
                {"name": "konsole", "command": ["konsole", "-e"]},
                {"name": "xfce4-terminal", "command": ["xfce4-terminal", "-e"]},
                {
                    "name": "io.elementary.terminal",
                    "command": ["io.elementary.terminal", "-e"],
                },
                {
                    "name": "x-terminal-emulator",
                    "command": ["x-terminal-emulator", "-e"],
                },
                {"name": "xterm", "command": ["xterm", "-e"]},
                {"name": "Terminal", "command": ["open", "-a", "Terminal"]},
                {"name": "alacritty", "command": ["alacritty", "-e"]},
                {"name": "kitty", "command": ["kitty"]},
                {"name": "terminator", "command": ["terminator", "-e"]},
            ]

        emulators: list[TerminalEmulator] = []
        for config in emulator_configs:
            if isinstance(config, dict) and "name" in config and "command" in config:
                emulator: TerminalEmulator = {
                    "name": config["name"],
                    "command": config["command"],
                }
                emulators.append(emulator)

        return emulators


# Global configuration instance (backwards compatibility)
config = ServerConfig.from_config_and_environment()
