"""
Main application class that orchestrates all components and threads
"""

import sys
import time
import logging
import threading
from typing import Optional, Dict, Any
from pathlib import Path

from ..config.settings import AppSettings
from ..config.manager import ConfigManager
from ..database.manager import DatabaseManager
from .message_bus import MessageBus, Message, MessageType, MessageBuilder
from .thread_manager import ThreadManager

logger = logging.getLogger(__name__)


class MbetterClientApplication:
    """Main application class that coordinates all components"""
    
    def __init__(self, settings: AppSettings):
        self.settings = settings
        self.running = False
        self.shutdown_event = threading.Event()
        
        # Core components
        self.db_manager: Optional[DatabaseManager] = None
        self.config_manager: Optional[ConfigManager] = None
        self.message_bus: Optional[MessageBus] = None
        self.thread_manager: Optional[ThreadManager] = None
        
        # Component references
        self.qt_player = None
        self.web_dashboard = None
        self.api_client = None
        
        # Main loop thread
        self._main_loop_thread: Optional[threading.Thread] = None
        
        logger.info("MbetterClient application initialized")
    
    def initialize(self) -> bool:
        """Initialize all application components"""
        try:
            logger.info("Initializing MbetterClient application...")
            
            # Initialize database manager
            if not self._initialize_database():
                return False
            
            # Initialize configuration manager
            if not self._initialize_config():
                return False
            
            # Initialize message bus
            if not self._initialize_message_bus():
                return False
            
            # Initialize thread manager
            if not self._initialize_thread_manager():
                return False
            
            # Initialize components based on settings
            if not self._initialize_components():
                return False
            
            logger.info("MbetterClient application initialized successfully")
            return True
            
        except Exception as e:
            logger.error(f"Failed to initialize application: {e}")
            return False
    
    def _initialize_database(self) -> bool:
        """Initialize database manager"""
        try:
            db_path = self.settings.database.get_absolute_path()
            self.db_manager = DatabaseManager(str(db_path))
            
            if not self.db_manager.initialize():
                logger.error("Database manager initialization failed")
                return False
            
            logger.info("Database manager initialized")
            return True
            
        except Exception as e:
            logger.error(f"Database initialization failed: {e}")
            return False
    
    def _initialize_config(self) -> bool:
        """Initialize configuration manager"""
        try:
            self.config_manager = ConfigManager(self.db_manager)
            
            if not self.config_manager.initialize():
                logger.error("Configuration manager initialization failed")
                return False
            
            # Update settings from database
            stored_settings = self.config_manager.get_settings()
            if stored_settings:
                # Merge runtime settings with stored settings (command line overrides database)
                stored_settings.fullscreen = self.settings.fullscreen
                stored_settings.web_host = self.settings.web_host
                stored_settings.web_port = self.settings.web_port
                stored_settings.database_path = self.settings.database_path
                stored_settings.enable_qt = self.settings.enable_qt
                stored_settings.enable_web = self.settings.enable_web
                
                self.settings = stored_settings
                
                # Re-sync runtime settings to component configs
                self.settings.qt.fullscreen = self.settings.fullscreen
                self.settings.web.host = self.settings.web_host
                self.settings.web.port = self.settings.web_port
                if self.settings.database_path:
                    self.settings.database.path = self.settings.database_path
            
            logger.info("Configuration manager initialized")
            return True
            
        except Exception as e:
            logger.error(f"Configuration initialization failed: {e}")
            return False
    
    def _initialize_message_bus(self) -> bool:
        """Initialize message bus"""
        try:
            self.message_bus = MessageBus(max_queue_size=1000)
            
            # Register core component
            self.message_bus.register_component("core")
            
            # Subscribe to system messages
            self.message_bus.subscribe("core", MessageType.SYSTEM_SHUTDOWN, self._handle_shutdown_message)
            self.message_bus.subscribe("core", MessageType.CONFIG_UPDATE, self._handle_config_update)
            self.message_bus.subscribe("core", MessageType.LOG_ENTRY, self._handle_log_entry)
            
            logger.info("Message bus initialized")
            return True
            
        except Exception as e:
            logger.error(f"Message bus initialization failed: {e}")
            return False
    
    def _initialize_thread_manager(self) -> bool:
        """Initialize thread manager"""
        try:
            self.thread_manager = ThreadManager(
                message_bus=self.message_bus,
                settings=self.settings
            )
            
            logger.info("Thread manager initialized")
            return True
            
        except Exception as e:
            logger.error(f"Thread manager initialization failed: {e}")
            return False
    
    def _initialize_components(self) -> bool:
        """Initialize application components based on settings"""
        try:
            components_initialized = 0
            
            # Initialize PyQt video player
            if self.settings.enable_qt:
                if self._initialize_qt_player():
                    components_initialized += 1
                else:
                    logger.error("Qt player initialization failed")
                    return False
            
            # Initialize web dashboard
            if self.settings.enable_web:
                if self._initialize_web_dashboard():
                    components_initialized += 1
                else:
                    logger.error("Web dashboard initialization failed")
                    return False
            
            # Initialize API client
            if self.settings.enable_api_client:
                if self._initialize_api_client():
                    components_initialized += 1
                else:
                    logger.error("API client initialization failed")
                    return False
            
            if components_initialized == 0:
                logger.error("No components were initialized")
                return False
            
            logger.info(f"{components_initialized} components initialized")
            return True
            
        except Exception as e:
            logger.error(f"Component initialization failed: {e}")
            return False
    
    def _initialize_qt_player(self) -> bool:
        """Initialize PyQt video player"""
        try:
            from ..qt_player.player import QtVideoPlayer
            
            self.qt_player = QtVideoPlayer(
                message_bus=self.message_bus,
                settings=self.settings.qt
            )
            
            # Register with thread manager
            self.thread_manager.register_component("qt_player", self.qt_player)
            
            logger.info("Qt player initialized")
            return True
            
        except Exception as e:
            logger.error(f"Qt player initialization failed: {e}")
            return False
    
    def _initialize_web_dashboard(self) -> bool:
        """Initialize web dashboard"""
        try:
            from ..web_dashboard.app import WebDashboard
            
            self.web_dashboard = WebDashboard(
                message_bus=self.message_bus,
                db_manager=self.db_manager,
                config_manager=self.config_manager,
                settings=self.settings.web
            )
            
            # Register with thread manager
            self.thread_manager.register_component("web_dashboard", self.web_dashboard)
            
            logger.info("Web dashboard initialized")
            return True
            
        except Exception as e:
            logger.error(f"Web dashboard initialization failed: {e}")
            return False
    
    def _initialize_api_client(self) -> bool:
        """Initialize API client"""
        try:
            from ..api_client.client import APIClient
            
            self.api_client = APIClient(
                message_bus=self.message_bus,
                db_manager=self.db_manager,
                config_manager=self.config_manager,
                settings=self.settings.api
            )
            
            # Register with thread manager
            self.thread_manager.register_component("api_client", self.api_client)
            
            logger.info("API client initialized")
            return True
            
        except Exception as e:
            logger.error(f"API client initialization failed: {e}")
            return False
    
    def run(self) -> int:
        """Run the application"""
        try:
            logger.info("Starting MbetterClient application...")
            
            self.running = True
            
            # Start all components
            if not self.thread_manager.start_all():
                logger.error("Failed to start components")
                return 1
            
            # Start main loop in separate thread
            self._main_loop_thread = threading.Thread(
                target=self._main_loop,
                name="MainLoop",
                daemon=False
            )
            self._main_loop_thread.start()
            
            # Send system ready message
            ready_message = MessageBuilder.system_status(
                sender="core",
                status="ready",
                details={
                    "components": self.thread_manager.get_component_names(),
                    "version": self.settings.version
                }
            )
            self.message_bus.publish(ready_message, broadcast=True)
            
            logger.info("MbetterClient application started successfully")
            
            # Wait for shutdown with a timeout to prevent indefinite hanging
            while self.running and not self.shutdown_event.is_set():
                self.shutdown_event.wait(timeout=1.0)
            
            logger.info("Application shutdown initiated")
            return self._cleanup()
            
        except KeyboardInterrupt:
            logger.info("Application interrupted by user")
            return self._cleanup()
        except Exception as e:
            logger.error(f"Application run failed: {e}")
            return self._cleanup()
    
    def _main_loop(self):
        """Main application loop for message processing and coordination"""
        logger.info("Main loop started")
        
        last_stats_time = time.time()
        stats_interval = 60  # Log stats every minute
        
        try:
            while self.running and not self.shutdown_event.is_set():
                try:
                    # Process messages
                    message = self.message_bus.get_message("core", timeout=1.0)
                    if message:
                        self._process_core_message(message)
                    
                    # Periodic tasks
                    current_time = time.time()
                    
                    # Log statistics
                    if current_time - last_stats_time >= stats_interval:
                        self._log_statistics()
                        last_stats_time = current_time
                    
                    # Placeholder for additional tasks
                    self._run_additional_tasks()
                    
                    # Check component health
                    self._check_component_health()
                    
                except Exception as e:
                    logger.error(f"Main loop error: {e}")
                    time.sleep(1)  # Prevent tight error loop
                    
        except Exception as e:
            logger.error(f"Main loop fatal error: {e}")
        
        logger.info("Main loop ended")
    
    def _process_core_message(self, message: Message):
        """Process messages received by the core component"""
        try:
            logger.debug(f"Core processing message: {message}")
            
            if message.type == MessageType.SYSTEM_STATUS:
                self._handle_system_status(message)
            elif message.type == MessageType.SYSTEM_ERROR:
                self._handle_system_error(message)
            elif message.type == MessageType.CONFIG_REQUEST:
                self._handle_config_request(message)
            else:
                logger.debug(f"Unhandled message type in core: {message.type}")
                
        except Exception as e:
            logger.error(f"Failed to process core message: {e}")
    
    def _handle_system_status(self, message: Message):
        """Handle system status messages"""
        try:
            status = message.data.get("status", "unknown")
            details = message.data.get("details", {})
            
            logger.info(f"System status from {message.sender}: {status}")
            
            if status == "error":
                logger.warning(f"Component error: {details}")
            elif status == "ready":
                logger.info(f"Component ready: {message.sender}")
                
        except Exception as e:
            logger.error(f"Failed to handle system status: {e}")
    
    def _handle_system_error(self, message: Message):
        """Handle system error messages"""
        try:
            error_msg = message.data.get("error", "Unknown error")
            details = message.data.get("details", {})
            
            logger.error(f"System error from {message.sender}: {error_msg}")
            logger.error(f"Error details: {details}")
            
            # TODO: Implement error recovery strategies
            
        except Exception as e:
            logger.error(f"Failed to handle system error: {e}")
    
    def _handle_config_request(self, message: Message):
        """Handle configuration requests"""
        try:
            requested_section = message.data.get("section", "all")
            
            if requested_section == "all":
                config_data = self.config_manager.get_web_config_dict()
            else:
                config_data = getattr(self.settings, requested_section, {})
                if hasattr(config_data, "__dict__"):
                    config_data = config_data.__dict__
            
            # Send response
            response = MessageBuilder.config_update(
                sender="core",
                config_section=requested_section,
                config_data=config_data
            )
            response.recipient = message.sender
            response.correlation_id = message.correlation_id
            
            self.message_bus.publish(response)
            
        except Exception as e:
            logger.error(f"Failed to handle config request: {e}")
    
    def _handle_shutdown_message(self, message: Message):
        """Handle shutdown message"""
        logger.info(f"Shutdown message received from {message.sender}")
        self.shutdown()
    
    def _handle_config_update(self, message: Message):
        """Handle configuration update message"""
        try:
            config_section = message.data.get("config_section")
            config_data = message.data.get("config_data")
            
            logger.info(f"Configuration update for section: {config_section}")
            
            # Update configuration
            if self.config_manager.update_from_web(config_data):
                logger.info("Configuration updated successfully")
                
                # Broadcast update to all components
                update_message = MessageBuilder.config_update(
                    sender="core",
                    config_section=config_section,
                    config_data=config_data
                )
                self.message_bus.publish(update_message, broadcast=True)
            else:
                logger.error("Configuration update failed")
                
        except Exception as e:
            logger.error(f"Failed to handle config update: {e}")
    
    def _handle_log_entry(self, message: Message):
        """Handle log entry message"""
        try:
            level = message.data.get("level", "INFO")
            log_message = message.data.get("message", "")
            details = message.data.get("details", {})
            
            # Add to database log
            self.db_manager.add_log_entry(
                level=level,
                component=message.sender,
                message=log_message,
                details=details
            )
            
        except Exception as e:
            logger.error(f"Failed to handle log entry: {e}")
    
    def _run_additional_tasks(self):
        """Placeholder for additional periodic tasks"""
        # TODO: Implement additional tasks as requested by user
        # This is where future extensions can be added
        pass
    
    def _check_component_health(self):
        """Check health of all components"""
        try:
            if self.thread_manager:
                unhealthy_components = self.thread_manager.get_unhealthy_components()
                
                if unhealthy_components:
                    logger.warning(f"Unhealthy components detected: {unhealthy_components}")
                    
                    # TODO: Implement component restart logic
                    
        except Exception as e:
            logger.error(f"Component health check failed: {e}")
    
    def _log_statistics(self):
        """Log application statistics"""
        try:
            if self.message_bus and self.thread_manager:
                msg_stats = self.message_bus.get_statistics()
                thread_stats = self.thread_manager.get_statistics()
                
                logger.info(f"Message bus stats: {msg_stats['total_components']} components, "
                           f"{msg_stats['total_messages_in_history']} messages")
                logger.info(f"Thread manager stats: {thread_stats['total_threads']} threads, "
                           f"{thread_stats['running_threads']} running")
                
        except Exception as e:
            logger.error(f"Failed to log statistics: {e}")
    
    def shutdown(self):
        """Shutdown the application"""
        logger.info("Application shutdown requested")
        
        self.running = False
        self.shutdown_event.set()
        
        # Send shutdown message to all components
        if self.message_bus:
            shutdown_message = Message(
                type=MessageType.SYSTEM_SHUTDOWN,
                sender="core",
                data={"reason": "Application shutdown"}
            )
            self.message_bus.publish(shutdown_message, broadcast=True)
    
    def _cleanup(self) -> int:
        """Cleanup application resources"""
        logger.info("Cleaning up application resources...")
        
        try:
            # Stop thread manager with shorter timeout
            if self.thread_manager:
                self.thread_manager.stop_all()
                self.thread_manager.wait_for_shutdown(timeout=3.0)
            
            # Wait for main loop thread with shorter timeout
            if self._main_loop_thread and self._main_loop_thread.is_alive():
                self._main_loop_thread.join(timeout=2.0)
                if self._main_loop_thread.is_alive():
                    logger.warning("Main loop thread did not shut down gracefully")
            
            # Shutdown message bus
            if self.message_bus:
                self.message_bus.shutdown()
            
            # Close database
            if self.db_manager:
                self.db_manager.close()
            
            logger.info("Application cleanup completed")
            return 0
            
        except Exception as e:
            logger.error(f"Cleanup failed: {e}")
            return 1
    
    def get_status(self) -> Dict[str, Any]:
        """Get current application status"""
        try:
            status = {
                "running": self.running,
                "version": self.settings.version,
                "components": {},
                "message_bus": {},
                "threads": {}
            }
            
            if self.thread_manager:
                status["threads"] = self.thread_manager.get_statistics()
                status["components"] = {
                    name: "running" if self.thread_manager.is_component_running(name) else "stopped"
                    for name in self.thread_manager.get_component_names()
                }
            
            if self.message_bus:
                status["message_bus"] = self.message_bus.get_statistics()
            
            return status
            
        except Exception as e:
            logger.error(f"Failed to get status: {e}")
            return {"error": str(e)}