"""Core module containing main application components."""

from __future__ import annotations

from dataclasses import dataclass
from functools import cached_property
from typing import Any, Protocol

from PySide2.QtCore import QObject, Signal

from pytola_sim.lscsim.utils.config_manager import config_manager
from pytola_sim.lscsim.utils.logger import get_logger

logger = get_logger(__name__)


class MessageHandler(Protocol):
    """Protocol for message handlers."""

    def handle_message(self, message: str, value: int, data: object) -> None:
        """Handle a message with given value and data."""
        ...


@dataclass
class AppConfiguration:
    """Application configuration dataclass."""

    database_host: str = "localhost"
    database_port: int = 5432
    database_name: str = "lscsim.db"
    user_level: str = "normal"  # normal, advanced, admin

    @cached_property
    def connection_string(self) -> str:
        """Generate database connection string."""
        return f"sqlite:///{self.database_name}"


class MainController(QObject):
    """Main application controller - equivalent to C++ MainCtrl."""

    # Signals for UI updates
    status_message_changed = Signal(str)
    component_loaded = Signal(str)
    user_level_changed = Signal(str)

    def __init__(self) -> None:
        super().__init__()
        self._components: dict[str, Any] = {}
        self._commands: dict[str, Any] = {}
        self._messages: dict[str, Any] = {}
        self.config = AppConfiguration()
        self.app_config = config_manager
        self._initialize_components()
        logger.info("Main controller initialized")

    def _initialize_components(self) -> None:
        """Initialize all system components."""
        # Register core components
        # Note: These will be implemented in later steps
        logger.info("Initializing system components")

    def _register_component(self, name: str, component: Any) -> None:  # noqa: ANN401
        """Register a component with the system."""
        self._components[name] = component
        logger.info(f"Registered component: {name}")
        self.component_loaded.emit(name)

    def execute_command(self, command_id: str, input_param: object = None) -> object | None:
        """Execute a command - equivalent to ExecuteCommand in C++."""
        logger.debug(f"Executing command: {command_id}")
        if command_id in self._commands:
            component = self._commands[command_id]
            try:
                result = component.execute(input_param)
                logger.info(f"Command {command_id} executed successfully")
            except (OSError, RuntimeError, ValueError):
                logger.exception(f"Command {command_id} failed")
                raise
        else:
            error_msg = f"Unknown command: {command_id}"
            logger.error(error_msg)
            raise ValueError(error_msg)
        return result

    def send_message(self, message: str, value: int = 0, data: object = None) -> None:
        """Send message to components - equivalent to SendMessage in C++."""
        logger.debug(f"Sending message: {message}")
        if message in self._messages:
            for handler in self._messages[message]:
                # Extract try-except out of loop to avoid performance overhead
                self._safe_handle_message(handler, message, value, data)

    def _safe_handle_message(
        self,
        handler: MessageHandler,
        message: str,
        value: int,
        data: object,
    ) -> None:
        """Safely handle message with exception handling."""
        try:
            handler.handle_message(message, value, data)
        except (OSError, RuntimeError, ValueError):
            logger.exception(f"Error handling message {message}")

    def get_component(self, component_id: str) -> object | None:
        """Get component by ID - equivalent to GetComponent in C++."""
        component = self._components.get(component_id)
        if component:
            logger.debug(f"Retrieved component: {component_id}")
        else:
            logger.warning(f"Component not found: {component_id}")
        return component

    def set_status_message(self, message: str) -> None:
        """Set status message for UI."""
        logger.info(f"Status message: {message}")
        self.status_message_changed.emit(message)

    def set_user_level(self, level: str) -> None:
        """Set user access level."""
        valid_levels = ["normal", "advanced", "admin"]
        if level in valid_levels:
            self.config.user_level = level
            logger.info(f"User level changed to: {level}")
            self.user_level_changed.emit(level)
        else:
            logger.error(f"Invalid user level: {level}")
            msg = f"Invalid user level: {level}"
            raise ValueError(msg)
