"""
PyQt6 Multi-threaded Video Player with QWebEngineView Overlay System
Replaces the previous PyQt5 implementation with enhanced functionality
"""

import sys
import time
import logging
import json
import threading
import signal
from pathlib import Path
from typing import Optional, Dict, Any, List
from PyQt6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QLabel, QPushButton, QSlider, QFrame, QStackedWidget
)
from PyQt6.QtCore import (
    Qt, QTimer, QThread, pyqtSignal, QUrl, QRect, QPropertyAnimation,
    QEasingCurve, QSequentialAnimationGroup, QObject, QMutex, QMutexLocker,
    QThreadPool, QRunnable, pyqtSlot
)
from PyQt6.QtGui import (
    QFont, QPainter, QPen, QBrush, QColor, QPixmap, QMovie, 
    QLinearGradient, QFontMetrics, QAction
)
from PyQt6.QtMultimedia import QMediaPlayer, QAudioOutput
from PyQt6.QtMultimediaWidgets import QVideoWidget
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEngineProfile
from PyQt6.QtWebChannel import QWebChannel

from ..core.thread_manager import ThreadedComponent
from ..core.message_bus import MessageBus, Message, MessageType, MessageBuilder
from ..config.settings import QtConfig
from .overlay_url_handler import OverlayUrlSchemeHandler

logger = logging.getLogger(__name__)


class OverlayWebChannel(QObject):
    """QObject for WebChannel communication with overlay HTML/JS"""
    
    # Signals to send data to JavaScript
    dataUpdated = pyqtSignal(dict)
    positionChanged = pyqtSignal(float, float)  # position, duration in seconds
    videoInfoChanged = pyqtSignal(dict)
    
    def __init__(self):
        super().__init__()
        self.mutex = QMutex()
        self.overlay_data = {}
        logger.info("OverlayWebChannel initialized")
    
    @pyqtSlot(str)
    def updateTitle(self, title: str):
        """Update main title from JavaScript"""
        # Clean data before emitting to prevent null emissions
        if title is None or title == "":
            logger.debug("Skipping null/empty title update")
            return
            
        with QMutexLocker(self.mutex):
            self.overlay_data['title'] = title
            cleaned_data = self._clean_data({'title': title})
            if cleaned_data:
                self.dataUpdated.emit(cleaned_data)
                logger.debug(f"Title updated: {title}")
    
    @pyqtSlot(str)
    def updateSubtitle(self, subtitle: str):
        """Update subtitle from JavaScript"""
        # Clean data before emitting to prevent null emissions
        if subtitle is None or subtitle == "":
            logger.debug("Skipping null/empty subtitle update")
            return
            
        with QMutexLocker(self.mutex):
            self.overlay_data['subtitle'] = subtitle
            cleaned_data = self._clean_data({'subtitle': subtitle})
            if cleaned_data:
                self.dataUpdated.emit(cleaned_data)
                logger.debug(f"Subtitle updated: {subtitle}")
    
    @pyqtSlot(bool)
    def toggleStats(self, show: bool):
        """Toggle stats panel visibility"""
        # Clean data before emitting to prevent null emissions
        if show is None:
            logger.debug("Skipping null show stats value")
            return
            
        with QMutexLocker(self.mutex):
            self.overlay_data['showStats'] = show
            cleaned_data = self._clean_data({'showStats': show})
            if cleaned_data:
                self.dataUpdated.emit(cleaned_data)
                logger.debug(f"Stats panel toggled: {show}")
    
    def send_data_update(self, data: Dict[str, Any]):
        """Send data update to JavaScript (thread-safe)"""
        # Validate data before sending to prevent null emissions
        if not data:
            logger.warning("send_data_update called with null/empty data, skipping")
            return
        
        # Debug original data before cleaning
        logger.debug(f"OverlayWebChannel received data: {data}, type: {type(data)}")
        
        # Clean data to remove null/undefined values before sending to JavaScript
        cleaned_data = self._clean_data(data)
        if not cleaned_data:
            logger.debug("All data properties were null/undefined, skipping JavaScript update")
            return
        
        # Debug what data is being sent to JavaScript
        logger.debug(f"OverlayWebChannel sending to JavaScript: {cleaned_data}")
        logger.debug(f"Data type: {type(cleaned_data)}, Data is dict: {isinstance(cleaned_data, dict)}")
        
        with QMutexLocker(self.mutex):
            self.overlay_data.update(cleaned_data)
            # Add additional validation just before emit
            if cleaned_data and isinstance(cleaned_data, dict) and any(v is not None for v in cleaned_data.values()):
                self.dataUpdated.emit(cleaned_data)
                logger.debug(f"Signal emitted successfully with data: {cleaned_data}")
            else:
                logger.warning(f"Prevented emission of invalid data: {cleaned_data}")
    
    def _clean_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Clean data by removing null/undefined values before sending to JavaScript"""
        cleaned_data = {}
        
        for key, value in data.items():
            # Skip null, undefined, or empty string values
            if value is None or value == "":
                logger.debug(f"OverlayWebChannel: Skipping null/empty property '{key}'")
                continue
            
            # Convert non-null values to appropriate types
            if isinstance(value, str):
                cleaned_data[key] = value
            elif isinstance(value, bool):
                cleaned_data[key] = value
            elif isinstance(value, (int, float)):
                cleaned_data[key] = str(value)  # Convert numbers to strings for overlay
            else:
                # For other types, convert to string
                cleaned_data[key] = str(value)
        
        return cleaned_data
    
    def send_position_update(self, position: float, duration: float):
        """Send playback position update to JavaScript (thread-safe)"""
        # Validate position and duration before emitting
        if position is None or duration is None:
            logger.debug("Skipping null position/duration update")
            return
        self.positionChanged.emit(position, duration)
    
    def send_video_info(self, info: Dict[str, Any]):
        """Send video information to JavaScript (thread-safe)"""
        # Validate info before emitting
        if not info:
            logger.debug("Skipping null/empty video info update")
            return
        cleaned_info = self._clean_data(info)
        if cleaned_info:
            self.videoInfoChanged.emit(cleaned_info)
        else:
            logger.debug("Video info contained only null/undefined values, skipping")


class VideoProcessingWorker(QRunnable):
    """Background worker for video processing tasks"""
    
    def __init__(self, task_type: str, data: Dict[str, Any], callback=None):
        super().__init__()
        self.task_type = task_type
        self.data = data
        self.callback = callback
        self.setAutoDelete(True)
    
    def run(self):
        """Execute the video processing task"""
        try:
            logger.debug(f"Processing video task: {self.task_type}")
            
            if self.task_type == "metadata_extraction":
                result = self._extract_metadata()
            elif self.task_type == "thumbnail_generation":
                result = self._generate_thumbnail()
            elif self.task_type == "overlay_rendering":
                result = self._render_overlay()
            else:
                result = {"error": f"Unknown task type: {self.task_type}"}
            
            if self.callback:
                self.callback(result)
                
        except Exception as e:
            logger.error(f"Video processing worker error: {e}")
            if self.callback:
                self.callback({"error": str(e)})
    
    def _extract_metadata(self) -> Dict[str, Any]:
        """Extract video metadata"""
        # Placeholder for metadata extraction
        return {
            "resolution": "1920x1080",
            "bitrate": "5.2 Mbps",
            "codec": "H.264",
            "fps": "30.0"
        }
    
    def _generate_thumbnail(self) -> Dict[str, Any]:
        """Generate video thumbnail"""
        # Placeholder for thumbnail generation
        return {"thumbnail_path": "/tmp/thumbnail.jpg"}
    
    def _render_overlay(self) -> Dict[str, Any]:
        """Render overlay elements"""
        # Placeholder for overlay rendering
        return {"rendered": True}


class OverlayWebView(QWebEngineView):
    """Custom QWebEngineView for video overlays with transparent background"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.web_channel = None
        self.overlay_channel = None
        self.current_template = "default.html"
        
        # Built-in templates directory (bundled with app)
        self.builtin_templates_dir = Path(__file__).parent / "templates"
        
        # Persistent uploaded templates directory (user data)
        self.uploaded_templates_dir = self._get_persistent_templates_dir()
        self.uploaded_templates_dir.mkdir(parents=True, exist_ok=True)
        
        # Primary templates directory for backwards compatibility
        self.templates_dir = self.builtin_templates_dir
        
        self.setup_web_view()
        self._setup_custom_scheme()
        logger.info(f"OverlayWebView initialized - builtin: {self.builtin_templates_dir}, uploaded: {self.uploaded_templates_dir}")
    
    def setup_web_view(self):
        """Setup web view with proper transparency for overlay"""
        logger.debug("OverlayWebView.setup_web_view() - Starting setup")
        
        # Set transparent background on the web page
        page = self.page()
        page.setBackgroundColor(QColor(0, 0, 0, 0))  # Transparent page background
        
        # Widget should be visible but allow transparency
        self.setStyleSheet("""
            QWebEngineView {
                border: none;
                background: transparent;
            }
        """)
        
        logger.debug("OverlayWebView setup completed with transparent page background")
        
        # Setup WebChannel
        self.web_channel = QWebChannel()
        self.overlay_channel = OverlayWebChannel()
        self.web_channel.registerObject("overlay", self.overlay_channel)
        page.setWebChannel(self.web_channel)
        
        # Load default template
        self.load_template(self.current_template)
    
    def _setup_custom_scheme(self):
        """Setup custom URL scheme handler for overlay resources"""
        try:
            # Get the page's profile
            profile = self.page().profile()
            
            # Create and install URL scheme handler
            self.scheme_handler = OverlayUrlSchemeHandler(self)
            profile.installUrlSchemeHandler(b"overlay", self.scheme_handler)
            
            logger.info("Custom overlay:// URL scheme handler installed successfully")
            
        except Exception as e:
            logger.error(f"Failed to setup custom URL scheme: {e}")
    
    def load_template(self, template_name: str):
        """Load a specific template file, prioritizing uploaded templates"""
        try:
            # If no template name provided, use default
            if not template_name:
                template_name = "default.html"
            
            # Ensure .html extension
            if not template_name.endswith('.html'):
                template_name += '.html'
            
            # First try uploaded templates directory (user uploads take priority)
            template_path = self.uploaded_templates_dir / template_name
            template_source = "uploaded"
            
            # If not found in uploaded, try built-in templates
            if not template_path.exists():
                template_path = self.builtin_templates_dir / template_name
                template_source = "builtin"
            
            # If still not found, fallback to default.html in built-in templates
            if not template_path.exists():
                default_template_path = self.builtin_templates_dir / "default.html"
                if default_template_path.exists():
                    template_path = default_template_path
                    template_name = "default.html"
                    template_source = "builtin"
                    logger.warning(f"Template {template_name} not found, using default.html")
                else:
                    logger.error(f"No template found: {template_name}, and default.html missing")
                    # Load fallback minimal overlay
                    self._load_fallback_overlay()
                    return
            
            if template_path and template_path.exists():
                self.load(QUrl.fromLocalFile(str(template_path)))
                self.current_template = template_name
                logger.info(f"Loaded template: {template_path} (source: {template_source})")
            else:
                logger.error(f"No template found: {template_name}")
                # Load fallback minimal overlay
                self._load_fallback_overlay()
                
        except Exception as e:
            logger.error(f"Failed to load template {template_name}: {e}")
            self._load_fallback_overlay()
    
    def reload_current_template(self):
        """Reload the current template"""
        self.load_template(self.current_template)
    
    def get_available_templates(self) -> List[str]:
        """Get list of available template files from both directories"""
        try:
            templates = set()
            
            # Get built-in templates
            if self.builtin_templates_dir.exists():
                builtin_templates = [f.name for f in self.builtin_templates_dir.glob("*.html")]
                templates.update(builtin_templates)
            
            # Get uploaded templates (these override built-in ones with same name)
            if self.uploaded_templates_dir.exists():
                uploaded_templates = [f.name for f in self.uploaded_templates_dir.glob("*.html")]
                templates.update(uploaded_templates)
            
            template_list = sorted(list(templates))
            if not template_list:
                template_list = ["default.html"]
            
            return template_list
            
        except Exception as e:
            logger.error(f"Failed to get available templates: {e}")
            return ["default.html"]
    
    def _get_persistent_templates_dir(self) -> Path:
        """Get persistent templates directory for user uploads"""
        try:
            import platform
            import os
            
            system = platform.system()
            
            if system == "Windows":
                # Use AppData/Roaming on Windows
                app_data = os.getenv('APPDATA', os.path.expanduser('~'))
                templates_dir = Path(app_data) / "MbetterClient" / "templates"
            elif system == "Darwin":  # macOS
                # Use ~/Library/Application Support on macOS
                templates_dir = Path.home() / "Library" / "Application Support" / "MbetterClient" / "templates"
            else:  # Linux and other Unix-like systems
                # Use ~/.config on Linux
                config_home = os.getenv('XDG_CONFIG_HOME', str(Path.home() / ".config"))
                templates_dir = Path(config_home) / "MbetterClient" / "templates"
            
            logger.debug(f"Persistent templates directory: {templates_dir}")
            return templates_dir
                
        except Exception as e:
            logger.error(f"Failed to determine persistent templates directory: {e}")
            # Fallback to local directory
            return Path.cwd() / "user_templates"
    
    def _get_default_template_path(self) -> Optional[Path]:
        """Get default template path from templates directory"""
        try:
            # Always use default.html from built-in templates directory
            default_template_path = self.builtin_templates_dir / "default.html"
            logger.debug(f"Default template path: {default_template_path}")
            return default_template_path
                
        except Exception as e:
            logger.error(f"Failed to determine default template path: {e}")
            return None
    
    def _load_fallback_overlay(self):
        """Load minimal fallback overlay if main overlay file not found"""
        try:
            fallback_html = """
            <!DOCTYPE html>
            <html>
            <head>
                <meta charset="UTF-8">
                <style>
                    body {
                        margin: 0;
                        padding: 20px;
                        background: transparent;
                        color: white;
                        font-family: Arial, sans-serif;
                    }
                    .overlay-content {
                        position: absolute;
                        top: 20px;
                        left: 20px;
                        background: rgba(0,0,0,0.7);
                        padding: 10px;
                        border-radius: 5px;
                    }
                </style>
            </head>
            <body>
                <div class="overlay-content">
                    <h2>MbetterClient PyQt6 Player</h2>
                    <p>Overlay System Active</p>
                </div>
            </body>
            </html>
            """
            
            self.setHtml(fallback_html)
            logger.info("Loaded fallback overlay HTML")
            
        except Exception as e:
            logger.error(f"Failed to load fallback overlay: {e}")
    
    def get_overlay_channel(self) -> OverlayWebChannel:
        """Get the overlay communication channel"""
        return self.overlay_channel
    
    def update_overlay_data(self, data: Dict[str, Any]):
        """Update overlay with new data"""
        # Validate data before sending to prevent null emissions
        if not data:
            logger.warning("update_overlay_data called with null/empty data, skipping")
            return
            
        if self.overlay_channel:
            self.overlay_channel.send_data_update(data)
    
    def update_position(self, position: float, duration: float):
        """Update playback position"""
        if self.overlay_channel:
            self.overlay_channel.send_position_update(position, duration)
    
    def update_video_info(self, info: Dict[str, Any]):
        """Update video information"""
        if self.overlay_channel:
            self.overlay_channel.send_video_info(info)


class NativeOverlayWidget(QWidget):
    """Native Qt overlay widget - no WebEngine to prevent freezing"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        # Initialize overlay_data BEFORE setup_ui() to prevent attribute errors
        self.overlay_data = {
            'title': 'MbetterClient PyQt6 Player',
            'subtitle': 'Ready for Content',
            'ticker': 'Welcome to MbetterClient • Professional Video Overlay System • Real-time Updates',
            'currentTime': '00:00:00'
        }
        # Add overlay_channel as None for compatibility with WebEngine interface
        self.overlay_channel = None
        self.setup_ui()
        logger.info("NativeOverlayWidget initialized")
    
    def setup_ui(self):
        """Setup native Qt overlay widgets with forced visibility"""
        logger.debug("NativeOverlayWidget.setup_ui() - Starting")
        
        # FORCE widget to be completely visible with bright background
        self.setStyleSheet("""
            NativeOverlayWidget {
                background-color: rgba(255, 0, 255, 200);
                border: 5px solid cyan;
            }
        """)
        
        # Ensure widget is visible
        self.setVisible(True)
        self.setAutoFillBackground(True)
        self.raise_()  # Bring to front
        
        # Main title label - ENHANCED VISIBILITY
        self.title_label = QLabel()
        self.title_label.setStyleSheet("""
            QLabel {
                color: white;
                font-size: 32px;
                font-weight: bold;
                background: rgba(0,0,0,0.8);
                padding: 15px;
                border-radius: 8px;
                border: 2px solid rgba(255,255,255,0.3);
                text-shadow: 2px 2px 4px rgba(0,0,0,1.0);
            }
        """)
        self.title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        self.title_label.setWordWrap(True)
        
        # Subtitle label - ENHANCED VISIBILITY
        self.subtitle_label = QLabel()
        self.subtitle_label.setStyleSheet("""
            QLabel {
                color: #ffffff;
                font-size: 18px;
                background: rgba(0,0,0,0.7);
                padding: 10px;
                border-radius: 5px;
                text-shadow: 1px 1px 2px rgba(0,0,0,1.0);
            }
        """)
        self.subtitle_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
        
        # Time display - ENHANCED VISIBILITY
        self.time_label = QLabel()
        self.time_label.setStyleSheet("""
            QLabel {
                color: yellow;
                font-size: 16px;
                font-weight: bold;
                background: rgba(0,0,0,0.8);
                padding: 8px;
                border-radius: 5px;
                border: 1px solid yellow;
            }
        """)
        
        # Ticker label - ENHANCED VISIBILITY
        self.ticker_label = QLabel()
        self.ticker_label.setStyleSheet("""
            QLabel {
                color: white;
                font-size: 14px;
                font-weight: bold;
                background: rgba(220, 53, 69, 0.95);
                padding: 12px;
                border-radius: 5px;
                border: 2px solid rgba(255,255,255,0.3);
            }
        """)
        self.ticker_label.setWordWrap(True)
        
        # Layout
        layout = QVBoxLayout(self)
        layout.setContentsMargins(20, 20, 20, 20)
        
        # Top section
        layout.addStretch(1)
        layout.addWidget(self.title_label)
        layout.addWidget(self.subtitle_label)
        layout.addStretch(2)
        
        # Bottom section
        layout.addWidget(self.ticker_label)
        layout.addSpacing(10)
        
        # Position time label in top-right corner
        self.time_label.setParent(self)
        
        # Update display with initial data
        self.update_display()
        
        logger.debug("NativeOverlayWidget UI setup completed")
    
    def resizeEvent(self, event):
        """Handle resize events"""
        super().resizeEvent(event)
        # Position time label in top-right corner
        if hasattr(self, 'time_label') and self.time_label:
            self.time_label.adjustSize()
            self.time_label.move(self.width() - self.time_label.width() - 20, 20)
    
    def update_overlay_data(self, data: Dict[str, Any]):
        """Update overlay display with new data"""
        try:
            updated = False
            
            if 'title' in data:
                self.overlay_data['title'] = data['title']
                updated = True
            
            if 'subtitle' in data:
                self.overlay_data['subtitle'] = data['subtitle']
                updated = True
            
            if 'ticker' in data:
                self.overlay_data['ticker'] = data['ticker']
                updated = True
            
            if 'currentTime' in data:
                self.overlay_data['currentTime'] = data['currentTime']
                updated = True
            
            if updated:
                self.update_display()
            
            logger.debug(f"Native overlay updated: {data}")
            
        except Exception as e:
            logger.error(f"Failed to update native overlay: {e}")
    
    def update_display(self):
        """Update all display elements"""
        try:
            self.title_label.setText(self.overlay_data.get('title', ''))
            self.subtitle_label.setText(self.overlay_data.get('subtitle', ''))
            self.ticker_label.setText(self.overlay_data.get('ticker', ''))
            self.time_label.setText(self.overlay_data.get('currentTime', ''))
            
            # Adjust time label position
            self.time_label.adjustSize()
            if self.width() > 0:
                self.time_label.move(self.width() - self.time_label.width() - 20, 20)
            
        except Exception as e:
            logger.error(f"Failed to update native overlay display: {e}")
    
    def update_position(self, position: float, duration: float):
        """Update playback position (compatible with WebEngine interface)"""
        try:
            current_time = self.format_time(position)
            total_time = self.format_time(duration)
            self.update_overlay_data({'currentTime': f"{current_time} / {total_time}"})
        except Exception as e:
            logger.error(f"Failed to update position in native overlay: {e}")
    
    def update_video_info(self, info: Dict[str, Any]):
        """Update video information (compatible with WebEngine interface)"""
        # Native overlay doesn't show detailed video stats by default
        # This is for compatibility with the WebEngine interface
        pass
    
    def format_time(self, seconds: float) -> str:
        """Format time in seconds to MM:SS"""
        mins = int(seconds // 60)
        secs = int(seconds % 60)
        return f"{mins:02d}:{secs:02d}"


class VideoWidget(QWidget):
    """Composite video widget with selectable overlay type"""
    
    def __init__(self, parent=None, use_native_overlay=False):
        super().__init__(parent)
        self.use_native_overlay = use_native_overlay
        self.setup_ui()
        logger.info(f"VideoWidget initialized (native_overlay={use_native_overlay})")
    
    def setup_ui(self):
        """Setup video player - ONLY video widget, overlay handled by PlayerWindow"""
        logger.debug("VideoWidget.setup_ui() - Setting up video-only widget")
        
        # Use a simple layout for the video widget only
        layout = QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        
        # ONLY the video widget - no overlays here
        self.video_widget = QVideoWidget(self)
        self.video_widget.setStyleSheet("QVideoWidget { background-color: black; }")
        layout.addWidget(self.video_widget)
        
        # No overlay_view created here - handled by PlayerWindow as separate window
        self.overlay_view = None
        
        logger.debug("VideoWidget setup completed - video only, overlay handled separately")
    
    def get_video_widget(self) -> QVideoWidget:
        """Get the video widget for media player"""
        return self.video_widget
    
    def get_overlay_view(self):
        """Get the overlay view - now handled by PlayerWindow as separate window"""
        return None  # Overlay is now a separate top-level window


class PlayerControlsWidget(QWidget):
    """Enhanced video player controls with thread-safe operations"""
    
    play_pause_clicked = pyqtSignal()
    stop_clicked = pyqtSignal()
    seek_requested = pyqtSignal(int)
    volume_changed = pyqtSignal(int)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.mutex = QMutex()
        self.setup_ui()
    
    def setup_ui(self):
        """Setup enhanced control UI"""
        layout = QHBoxLayout(self)
        layout.setContentsMargins(10, 5, 10, 5)
        
        # Play/Pause button
        self.play_pause_btn = QPushButton("⏸️")
        self.play_pause_btn.setFixedSize(40, 30)
        self.play_pause_btn.clicked.connect(self.play_pause_clicked.emit)
        
        # Stop button
        self.stop_btn = QPushButton("⏹️")
        self.stop_btn.setFixedSize(40, 30)
        self.stop_btn.clicked.connect(self.stop_clicked.emit)
        
        # Position slider
        self.position_slider = QSlider(Qt.Orientation.Horizontal)
        self.position_slider.setMinimum(0)
        self.position_slider.setMaximum(100)
        self.position_slider.sliderPressed.connect(self._on_slider_pressed)
        self.position_slider.sliderReleased.connect(self._on_slider_released)
        
        # Volume slider
        self.volume_slider = QSlider(Qt.Orientation.Horizontal)
        self.volume_slider.setMinimum(0)
        self.volume_slider.setMaximum(100)
        self.volume_slider.setValue(100)
        self.volume_slider.setMaximumWidth(100)
        self.volume_slider.valueChanged.connect(self.volume_changed.emit)
        
        # Time labels
        self.current_time_label = QLabel("00:00")
        self.duration_label = QLabel("00:00")
        
        # Volume label
        volume_label = QLabel("🔊")
        
        # Layout
        layout.addWidget(self.play_pause_btn)
        layout.addWidget(self.stop_btn)
        layout.addWidget(self.current_time_label)
        layout.addWidget(self.position_slider, 1)  # Stretch
        layout.addWidget(self.duration_label)
        layout.addWidget(volume_label)
        layout.addWidget(self.volume_slider)
        
        # Enhanced styling
        self.setStyleSheet("""
            QWidget {
                background-color: rgba(0, 0, 0, 200);
                color: white;
            }
            QPushButton {
                border: 1px solid #555;
                border-radius: 5px;
                background-color: #333;
                font-weight: bold;
            }
            QPushButton:hover {
                background-color: #555;
            }
            QPushButton:pressed {
                background-color: #777;
            }
            QSlider::groove:horizontal {
                border: 1px solid #555;
                height: 6px;
                background: #222;
                border-radius: 3px;
            }
            QSlider::handle:horizontal {
                background: #fff;
                border: 2px solid #555;
                width: 14px;
                margin: -5px 0;
                border-radius: 7px;
            }
            QSlider::handle:horizontal:hover {
                background: #ddd;
            }
        """)
        
        self.slider_pressed = False
    
    def _on_slider_pressed(self):
        with QMutexLocker(self.mutex):
            self.slider_pressed = True
    
    def _on_slider_released(self):
        with QMutexLocker(self.mutex):
            self.slider_pressed = False
            self.seek_requested.emit(self.position_slider.value())
    
    def update_position(self, position: int, duration: int):
        """Update position display (thread-safe)"""
        with QMutexLocker(self.mutex):
            if not self.slider_pressed and duration > 0:
                self.position_slider.setValue(int(position * 100 / duration))
            
            self.current_time_label.setText(self._format_time(position))
            self.duration_label.setText(self._format_time(duration))
    
    def update_play_pause_button(self, is_playing: bool):
        """Update play/pause button state (thread-safe)"""
        with QMutexLocker(self.mutex):
            self.play_pause_btn.setText("⏸️" if is_playing else "▶️")
    
    def _format_time(self, ms: int) -> str:
        """Format time in milliseconds to MM:SS"""
        seconds = ms // 1000
        minutes = seconds // 60
        seconds = seconds % 60
        return f"{minutes:02d}:{seconds:02d}"


class PlayerWindow(QMainWindow):
    """Enhanced main player window with thread-safe operations"""
    
    # Signals for thread communication
    position_changed = pyqtSignal(int, int)
    video_loaded = pyqtSignal(str)
    
    def __init__(self, settings: QtConfig, message_bus: MessageBus = None):
        super().__init__()
        self.settings = settings
        self.mutex = QMutex()
        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(4)
        self._message_bus = message_bus  # Store message bus reference for shutdown messages
        
        self.setup_ui()
        self.setup_media_player()
        self.setup_timers()
        
        logger.info("PlayerWindow initialized")
    
    def setup_ui(self):
        """Setup enhanced window UI"""
        self.setWindowTitle("MbetterClient - PyQt6 Video Player")
        
        # CLEAN INTERFACE: Remove all toolbars, menus, and status bars
        self.setMenuBar(None)  # Remove menu bar completely
        self.setStatusBar(None)  # Remove status bar
        self.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)  # Disable context menus
        
        # RADICAL APPROACH: Remove ALL transparency attributes and force complete opacity
        self.setAutoFillBackground(True)  # Critical: Fill background automatically
        
        # Use a simple black background color palette approach
        from PyQt6.QtGui import QPalette
        palette = self.palette()
        palette.setColor(QPalette.ColorRole.Window, QColor(0, 0, 0))  # Black background
        palette.setColor(QPalette.ColorRole.WindowText, QColor(255, 255, 255))  # White text
        palette.setColor(QPalette.ColorRole.Base, QColor(0, 0, 0))  # Black base
        self.setPalette(palette)
        
        # Simple black stylesheet
        self.setStyleSheet("QMainWindow { background-color: black; }")
        
        # Central widget - completely black and opaque
        central_widget = QWidget()
        central_widget.setAutoFillBackground(True)
        central_widget.setPalette(palette)
        central_widget.setStyleSheet("background-color: black;")
        self.setCentralWidget(central_widget)
        
        # Layout
        layout = QVBoxLayout(central_widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)

        # SIMPLE VIDEO WIDGET ONLY - overlay as separate top-level window
        overlay_type = "Native Qt Widgets" if self.settings.use_native_overlay else "QWebEngineView"
        logger.info(f"PlayerWindow: Overlay configuration - use_native_overlay={self.settings.use_native_overlay}, using {overlay_type}")
        
        # Create simple video widget without any overlay (VideoWidget no longer handles overlays)
        self.video_widget = VideoWidget(parent=central_widget, use_native_overlay=False)
        layout.addWidget(self.video_widget, 1)
        
        # THREADING FIXED: Re-enable overlay system with proper Qt main thread architecture
        # Create overlay as SEPARATE TOP-LEVEL WINDOW
        self.overlay_window = QWidget()
        self.overlay_window.setWindowFlags(
            Qt.WindowType.FramelessWindowHint |
            Qt.WindowType.WindowStaysOnTopHint |
            Qt.WindowType.Tool
        )
        self.overlay_window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
        self.overlay_window.setStyleSheet("background: transparent;")

        # Create overlay based on configuration - matching test_video_debug.py behavior
        if self.settings.use_native_overlay:
            self.window_overlay = NativeOverlayWidget(self.overlay_window)
            logger.debug("PlayerWindow: Created NativeOverlayWidget overlay as separate window")
        else:
            self.window_overlay = OverlayWebView(self.overlay_window)
            logger.debug("PlayerWindow: Created QWebEngineView overlay as separate window")

        # Layout for overlay window
        overlay_layout = QVBoxLayout(self.overlay_window)
        overlay_layout.setContentsMargins(0, 0, 0, 0)
        overlay_layout.addWidget(self.window_overlay)
        
        logger.info("OVERLAY SYSTEM RE-ENABLED: Threading conflicts resolved with Qt main thread architecture")
        
        # Controls removed per user request - clean overlay-only interface
        self.controls = None

        # Window settings - FORCE window to display properly
        if self.settings.fullscreen:
            self.showFullScreen()
        else:
            self.resize(self.settings.window_width, self.settings.window_height)
            
        # CRITICAL: Force window display and painting
        self.show()
        self.raise_()  # Bring to front
        self.activateWindow()  # Give focus
        self.repaint()  # Force repaint
        
        # Force central widget to repaint too
        if self.centralWidget():
            self.centralWidget().repaint()
        
        if self.settings.always_on_top:
            self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint)
            self.show()  # Reshow after flag change
        
        # Enable overlay window positioning - threading issues resolved
        logger.info("ENABLING overlay window with proper main thread positioning")
        QTimer.singleShot(100, self._sync_overlay_position)
        
        # Setup menu - REMOVED for clean interface
        # self.setup_menu()
    
    def _sync_overlay_position(self):
        """Synchronize overlay window position with main player window"""
        try:
            if hasattr(self, 'overlay_window') and self.overlay_window:
                # Get main window geometry
                main_geo = self.geometry()
                logger.debug(f"Main window geometry: {main_geo}")
                
                # Position overlay window exactly over main window
                self.overlay_window.setGeometry(main_geo)
                self.overlay_window.show()
                self.overlay_window.raise_()
                
                logger.debug(f"Overlay window positioned at: {self.overlay_window.geometry()}")
                
        except Exception as e:
            logger.error(f"Failed to sync overlay position: {e}")
    
    def resizeEvent(self, event):
        """Handle window resize events - sync overlay position"""
        super().resizeEvent(event)
        if hasattr(self, 'overlay_window') and self.overlay_window:
            # Delay sync to allow window to finish resizing
            QTimer.singleShot(10, self._sync_overlay_position)
    
    def moveEvent(self, event):
        """Handle window move events - sync overlay position"""
        super().moveEvent(event)
        if hasattr(self, 'overlay_window') and self.overlay_window:
            # Delay sync to allow window to finish moving
            QTimer.singleShot(10, self._sync_overlay_position)
    
    def setup_menu(self):
        """Setup application menu - DISABLED for clean interface"""
        # Menu and toolbar removed for minimal video player interface
        # All functionality still available via keyboard shortcuts:
        # - F11: Toggle fullscreen
        # - S: Toggle stats
        # - Space: Play/pause
        # - M: Mute/unmute
        # - Escape: Exit
        pass
    
    def setup_media_player(self):
        """Setup PyQt6 media player with audio output"""
        self.media_player = QMediaPlayer()
        self.audio_output = QAudioOutput()
        
        self.media_player.setAudioOutput(self.audio_output)
        
        # DEBUG: Check video widget setup
        logger.info(f"Setting up media player video output:")
        logger.info(f"self.video_widget: {self.video_widget}")
        logger.info(f"has get_video_widget: {hasattr(self.video_widget, 'get_video_widget')}")
        
        # For VideoWidget without internal overlay, get the base video widget directly
        if hasattr(self.video_widget, 'get_video_widget'):
            video_output_widget = self.video_widget.get_video_widget()
            logger.info(f"Using get_video_widget(): {video_output_widget}")
            logger.info(f"Video widget visible: {video_output_widget.isVisible()}")
            logger.info(f"Video widget size: {video_output_widget.size()}")
            self.media_player.setVideoOutput(video_output_widget)
        else:
            # Simple VideoWidget case
            video_output_widget = self.video_widget.video_widget
            logger.info(f"Using video_widget directly: {video_output_widget}")
            logger.info(f"Video widget visible: {video_output_widget.isVisible()}")
            logger.info(f"Video widget size: {video_output_widget.size()}")
            self.media_player.setVideoOutput(video_output_widget)
        
        logger.info(f"Media player video output set to: {self.media_player.videoOutput()}")
        
        # Connect signals
        self.media_player.playbackStateChanged.connect(self.on_state_changed)
        self.media_player.positionChanged.connect(self.on_position_changed)
        self.media_player.durationChanged.connect(self.on_duration_changed)
        self.media_player.errorOccurred.connect(self.on_media_error)
        self.media_player.mediaStatusChanged.connect(self.on_media_status_changed)
        
        # Set volume
        self.audio_output.setVolume(self.settings.volume)
        self.audio_output.setMuted(self.settings.mute)
    
    def setup_timers(self):
        """Setup various timers"""
        # Overlay update timer
        self.overlay_timer = QTimer()
        self.overlay_timer.timeout.connect(self.update_overlay_periodically)
        self.overlay_timer.start(1000)  # Update every second
        
        # Mouse tracking for window interactions (no controls to show/hide)
        self.setMouseTracking(True)
    
    def open_file_dialog(self):
        """Open file dialog to select video"""
        from PyQt6.QtWidgets import QFileDialog
        
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Open Video File",
            "",
            "Video Files (*.mp4 *.avi *.mkv *.mov *.wmv *.flv *.webm *.m4v);;All Files (*)"
        )
        
        if file_path:
            self.play_video(file_path)
    
    def play_video(self, file_path: str, template_data: Dict[str, Any] = None, template_name: str = None):
        """Play video file with optional overlay data and template"""
        try:
            logger.info(f"PlayerWindow.play_video() called with: {file_path}")
            logger.info(f"Media player state before play: {self.media_player.playbackState()}")
            logger.info(f"Media player error state: {self.media_player.error()}")
            
            with QMutexLocker(self.mutex):
                # Handle both absolute and relative file paths
                path_obj = Path(file_path)
                logger.info(f"Original path object: {path_obj}")
                
                if not path_obj.is_absolute():
                    # For relative paths, resolve them relative to the application directory
                    import os
                    app_dir = Path(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
                    absolute_path = app_dir / file_path
                    logger.info(f"Resolved relative path '{file_path}' to '{absolute_path}'")
                else:
                    absolute_path = path_obj
                    logger.info(f"Using absolute path: {absolute_path}")
                
                # Verify file exists before trying to play
                logger.info(f"Checking if file exists: {absolute_path}")
                if not absolute_path.exists():
                    logger.error(f"Video file not found: {absolute_path}")
                    # Show error in overlay
                    if hasattr(self, 'window_overlay'):
                        overlay_view = self.window_overlay
                        error_data = {
                            'title': 'File Not Found',
                            'subtitle': f'Cannot find: {file_path}',
                            'ticker': 'Please check the file path and try again.'
                        }
                        # Update overlay safely - handles both native and WebEngine
                        self._update_overlay_safe(overlay_view, error_data)
                    return
                
                logger.info(f"File exists! Size: {absolute_path.stat().st_size} bytes")
                
                url = QUrl.fromLocalFile(str(absolute_path))
                logger.info(f"Created QUrl: {url.toString()}")
                logger.info(f"QUrl is valid: {url.isValid()}")
                
                logger.info(f"Media player current state: {self.media_player.playbackState()}")
                self.media_player.setSource(url)
                logger.info(f"Media player source set to: {url.toString()}")
                
                # Load specified template or reload current template when playing a video
                if hasattr(self, 'window_overlay') and isinstance(self.window_overlay, OverlayWebView):
                    if template_name:
                        self.window_overlay.load_template(template_name)
                        logger.info(f"Loaded template '{template_name}' for video playback")
                    else:
                        self.window_overlay.reload_current_template()
                        logger.info("Reloaded current overlay template for video playback")
                
                # Update overlay with video info using safe method
                overlay_data = template_data or {}
                overlay_data.update({
                    'title': f'Playing: {Path(file_path).name}',
                    'subtitle': 'MbetterClient PyQt6 Player'
                })
                
                if hasattr(self, 'window_overlay'):
                    overlay_view = self.window_overlay
                    # Update overlay safely - handles both native and WebEngine
                    QTimer.singleShot(1000, lambda: self._update_overlay_safe(overlay_view, overlay_data))
                
                if self.settings.auto_play:
                    self.media_player.play()
                    
                # COMPREHENSIVE DEBUGGING: Compare Qt state with working test script
                video_output_widget = self.media_player.videoOutput()
                if video_output_widget:
                    logger.error("=== COMPREHENSIVE VIDEO WIDGET DEBUG ===")
                    
                    # Widget state debugging
                    logger.error(f"Video widget type: {type(video_output_widget)}")
                    logger.error(f"Video widget visible: {video_output_widget.isVisible()}")
                    logger.error(f"Video widget enabled: {video_output_widget.isEnabled()}")
                    logger.error(f"Video widget size: {video_output_widget.size()}")
                    logger.error(f"Video widget geometry: {video_output_widget.geometry()}")
                    logger.error(f"Video widget parent: {video_output_widget.parent()}")
                    logger.error(f"Video widget window: {video_output_widget.window()}")
                    logger.error(f"Video widget isWindow: {video_output_widget.isWindow()}")
                    
                    # Window state debugging
                    main_window = video_output_widget.window()
                    if main_window:
                        logger.error(f"Main window type: {type(main_window)}")
                        logger.error(f"Main window visible: {main_window.isVisible()}")
                        logger.error(f"Main window size: {main_window.size()}")
                        logger.error(f"Main window windowState: {main_window.windowState()}")
                        logger.error(f"Main window windowFlags: {main_window.windowFlags()}")
                        logger.error(f"Main window opacity: {main_window.windowOpacity()}")
                        logger.error(f"Main window autoFillBackground: {main_window.autoFillBackground()}")
                        
                    # Application state debugging
                    app = QApplication.instance()
                    if app:
                        logger.error(f"QApplication type: {type(app)}")
                        logger.error(f"QApplication activeWindow: {app.activeWindow()}")
                        logger.error(f"QApplication focusWidget: {app.focusWidget()}")
                        logger.error(f"QApplication topLevelWidgets: {[str(w) for w in app.topLevelWidgets()]}")
                        
                    # Parent chain debugging
                    parent = video_output_widget.parent()
                    chain = []
                    while parent:
                        chain.append(f"{type(parent).__name__}(visible={parent.isVisible()}, size={parent.size()})")
                        parent = parent.parent()
                    logger.error(f"Parent chain: {' -> '.join(chain)}")
                    
                    logger.error("=== END VIDEO WIDGET DEBUG ===")
                
                # CRITICAL: Reapply window focus fix for OpenGL context before video playback
                logger.error("REAPPLYING WINDOW FOCUS FIX: Ensuring OpenGL context for video rendering")
                app = QApplication.instance()
                if app:
                    self.show()
                    self.raise_()
                    self.activateWindow()
                    app.processEvents()
                    app.setActiveWindow(self)
                    logger.error(f"After playback focus fix - activeWindow: {app.activeWindow()}")
                    logger.error(f"After playback focus fix - focusWidget: {app.focusWidget()}")
                else:
                    logger.error("CRITICAL ERROR: No QApplication instance found!")
                
                # Start background metadata extraction
                worker = VideoProcessingWorker(
                    "metadata_extraction",
                    {"file_path": file_path},
                    self.on_metadata_extracted
                )
                self.thread_pool.start(worker)
                
                logger.info(f"Playing video: {file_path}")
                self.video_loaded.emit(file_path)
                
        except Exception as e:
            logger.error(f"Failed to play video: {e}")
    
    def on_metadata_extracted(self, metadata: Dict[str, Any]):
        """Handle extracted metadata"""
        if 'error' not in metadata and hasattr(self, 'window_overlay'):
            self.window_overlay.update_video_info(metadata)
            logger.debug(f"Video metadata: {metadata}")
    
    def toggle_play_pause(self):
        """Toggle play/pause (thread-safe)"""
        with QMutexLocker(self.mutex):
            if self.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState:
                self.media_player.pause()
            else:
                self.media_player.play()
    
    def stop_playback(self):
        """Stop playback (thread-safe)"""
        with QMutexLocker(self.mutex):
            self.media_player.stop()
    
    def seek_to_position(self, percentage: int):
        """Seek to position (percentage) (thread-safe)"""
        with QMutexLocker(self.mutex):
            duration = self.media_player.duration()
            if duration > 0:
                position = int(duration * percentage / 100)
                self.media_player.setPosition(position)
    
    def set_volume(self, volume: int):
        """Set volume (0-100) (thread-safe)"""
        with QMutexLocker(self.mutex):
            self.audio_output.setVolume(volume / 100.0)
    
    def toggle_fullscreen(self):
        """Toggle fullscreen mode"""
        if self.isFullScreen():
            self.showNormal()
        else:
            self.showFullScreen()
    
    def toggle_stats(self):
        """Toggle stats panel"""
        overlay_view = self.window_overlay
        # Toggle stats via WebChannel
        if hasattr(overlay_view, 'overlay_channel') and overlay_view.overlay_channel:
            current_stats = getattr(self, '_stats_visible', False)
            overlay_view.overlay_channel.toggleStats(not current_stats)
            self._stats_visible = not current_stats
    
    def on_state_changed(self, state):
        """Handle playback state changes (thread-safe)"""
        is_playing = state == QMediaPlayer.PlaybackState.PlayingState
        # Controls removed - no button updates needed
        logger.debug(f"Playback state changed: {'playing' if is_playing else 'paused'}")
    
    def on_position_changed(self, position):
        """Handle position changes (thread-safe)"""
        duration = self.media_player.duration()
        # Controls removed - no slider updates needed
        
        # Update overlay with position info using safe method
        if duration > 0 and hasattr(self, 'window_overlay'):
            overlay_view = self.window_overlay
            # Update position for both native and WebEngine overlays
            try:
                overlay_view.update_position(
                    position / 1000.0,  # Convert to seconds
                    duration / 1000.0   # Convert to seconds
                )
            except Exception as e:
                logger.debug(f"Failed to update overlay position: {e}")
        
        # Emit signal for other components
        self.position_changed.emit(position, duration)
    
    def on_duration_changed(self, duration):
        """Handle duration changes"""
        # Controls removed - no updates needed
        logger.debug(f"Duration changed: {duration}ms")
    
    def on_media_error(self, error):
        """Handle media errors"""
        logger.error(f"Media player error: {error}")
        
        # Show error in overlay using safe update
        if hasattr(self, 'window_overlay'):
            overlay_view = self.window_overlay
            error_data = {
                'title': 'Playback Error',
                'subtitle': f'Error: {error.name if hasattr(error, "name") else str(error)}',
                'ticker': 'Please check the video file and try again.'
            }
            # Update overlay safely - handles both native and WebEngine
            self._update_overlay_safe(overlay_view, error_data)
    
    def on_media_status_changed(self, status):
        """Handle media status changes"""
        logger.debug(f"Media status changed: {status}")
        
        if status == QMediaPlayer.MediaStatus.LoadedMedia:
            # Media loaded successfully - use safe update
            if hasattr(self, 'window_overlay'):
                overlay_view = self.window_overlay
                status_data = {'subtitle': 'Media loaded successfully'}
                # Update overlay safely - handles both native and WebEngine
                self._update_overlay_safe(overlay_view, status_data)
    
    def update_overlay_periodically(self):
        """Periodic overlay updates with WebEngine safety checks"""
        try:
            current_time = time.strftime("%H:%M:%S")
            if hasattr(self, 'window_overlay'):
                overlay_view = self.window_overlay
                # Only update if we have valid overlay data
                time_data = {'currentTime': current_time}
                if time_data and current_time:  # Ensure we have valid data
                    self._update_overlay_safe(overlay_view, time_data)
                
        except Exception as e:
            logger.error(f"Periodic overlay update failed: {e}")
    
    def mouseMoveEvent(self, event):
        """Show controls on mouse movement"""
        super().mouseMoveEvent(event)
        self.show_controls()
        
        # Controls removed - no timer handling needed
        pass
    
    def keyPressEvent(self, event):
        """Handle key presses"""
        if event.key() == Qt.Key.Key_Space:
            self.toggle_play_pause()
        elif event.key() == Qt.Key.Key_Escape:
            if self.isFullScreen():
                self.showNormal()
            else:
                self.close()
        elif event.key() == Qt.Key.Key_F11:
            self.toggle_fullscreen()
        elif event.key() == Qt.Key.Key_M:
            current_muted = self.audio_output.isMuted()
            self.audio_output.setMuted(not current_muted)
        elif event.key() == Qt.Key.Key_S:
            self.toggle_stats()
        
        super().keyPressEvent(event)
    
    def show_controls(self):
        """Show controls - REMOVED per user request"""
        # Controls removed - clean overlay-only interface
        self.setCursor(Qt.CursorShape.ArrowCursor)
    
    def hide_controls(self):
        """Hide controls - REMOVED per user request"""
        # Controls removed - clean overlay-only interface
        if self.isFullScreen():
            self.setCursor(Qt.CursorShape.BlankCursor)
    
    def start_controls_timer(self):
        """Start timer to hide controls - REMOVED per user request"""
        # Controls removed - no timer needed
        pass
    
    def closeEvent(self, event):
        """Handle window close (thread-safe)"""
        with QMutexLocker(self.mutex):
            self.media_player.stop()
            self.thread_pool.waitForDone(3000)  # Wait up to 3 seconds for threads
            
            # Close overlay window
            if hasattr(self, 'overlay_window') and self.overlay_window:
                self.overlay_window.close()
                logger.debug("Overlay window closed")
            
        logger.info("Player window closing - Qt will handle application exit")
        event.accept()
    
    def _is_webengine_ready(self, overlay_view):
        """Check if WebEngine overlay is fully ready for updates"""
        try:
            # First check if this is actually a WebEngine overlay
            if not isinstance(overlay_view, OverlayWebView):
                return False
                
            # Check if overlay_view is a QWebEngineView
            if not hasattr(overlay_view, 'page'):
                return False
                
            page = overlay_view.page()
            if not page:
                return False
            
            # Check if WebChannel exists
            if not page.webChannel():
                return False
                
            # Check if URL is loaded (not about:blank)
            current_url = overlay_view.url().toString()
            if current_url == 'about:blank' or not current_url:
                return False
            
            # Check if the overlay_channel object exists and is ready
            if not (hasattr(overlay_view, 'overlay_channel') and overlay_view.overlay_channel):
                return False
                
            return True
            
        except Exception as e:
            logger.debug(f"WebEngine readiness check failed: {e}")
            return False
    
    def _is_native_overlay(self, overlay_view):
        """Check if this is a native overlay"""
        return isinstance(overlay_view, NativeOverlayWidget)
    
    def _clean_overlay_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Clean overlay data by removing null/undefined values"""
        cleaned_data = {}
        
        # Define valid overlay properties and their expected types
        valid_properties = {
            'title': str,
            'subtitle': str,
            'ticker': str,
            'currentTime': str,
            'showStats': bool,
            'customCSS': str
        }
        
        for key, value in data.items():
            # Skip null, undefined, or empty string values
            if value is None or value == "":
                logger.debug(f"Skipping null/empty property '{key}' in overlay data")
                continue
            
            # Validate property type if it's a known property
            if key in valid_properties:
                expected_type = valid_properties[key]
                if not isinstance(value, expected_type):
                    logger.debug(f"Converting property '{key}' from {type(value)} to {expected_type}")
                    try:
                        # Convert to expected type
                        if expected_type == str:
                            cleaned_data[key] = str(value)
                        elif expected_type == bool:
                            cleaned_data[key] = bool(value)
                        else:
                            cleaned_data[key] = value
                    except (ValueError, TypeError):
                        logger.warning(f"Failed to convert property '{key}' to {expected_type}, skipping")
                        continue
                else:
                    cleaned_data[key] = value
            else:
                # Unknown property, pass through if not null
                cleaned_data[key] = value
        
        logger.debug(f"Cleaned overlay data: {cleaned_data}")
        return cleaned_data
    
    def _update_overlay_safe(self, overlay_view, data):
        """Update overlay data safely, handling both native and WebEngine overlays"""
        try:
            # Clean data before sending to prevent null property issues
            cleaned_data = self._clean_overlay_data(data)
            if not cleaned_data:
                logger.debug("No valid data to send to overlay after cleaning")
                return False
            
            if self._is_native_overlay(overlay_view):
                # Native overlay - always ready, update immediately
                overlay_view.update_overlay_data(cleaned_data)
                logger.debug("Native overlay updated successfully")
                return True
            elif isinstance(overlay_view, OverlayWebView):
                # WebEngine overlay - check readiness first
                if self._is_webengine_ready(overlay_view):
                    overlay_view.update_overlay_data(cleaned_data)
                    logger.debug("WebEngine overlay updated successfully")
                    return True
                else:
                    logger.debug("WebEngine overlay not ready, skipping update")
                    return False
            else:
                logger.warning(f"Unknown overlay type: {type(overlay_view)}")
                return False
        except Exception as e:
            logger.error(f"Failed to update overlay safely: {e}")
            return False
    
    def _send_safe_overlay_update(self, overlay_view, data):
        """Send overlay update with additional safety checks"""
        return self._update_overlay_safe(overlay_view, data)


class QtVideoPlayer(QObject):
    """PyQt6 video player component with message bus integration (replaces PyQt5 version)"""
    
    # Signal for cross-thread video playback
    play_video_signal = pyqtSignal(str, dict)
    
    def __init__(self, message_bus: MessageBus, settings: QtConfig):
        super().__init__()
        self.name = "qt_player"
        self.message_bus = message_bus
        self.settings = settings
        self.app: Optional[QApplication] = None
        self.window: Optional[PlayerWindow] = None
        self.mutex = QMutex()
        
        # Register message queue
        logger.info(f"Registering QtVideoPlayer component with message bus - name: '{self.name}'")
        self.message_queue = self.message_bus.register_component(self.name)
        logger.info(f"QtVideoPlayer component registered successfully - queue: {self.message_queue}")
        
        # Message processing timer (runs on Qt main thread)
        self.message_timer: Optional[QTimer] = None
        
        logger.info("QtVideoPlayer (PyQt6) initialized")
    
    def initialize(self) -> bool:
        """Initialize PyQt6 application and components"""
        try:
            with QMutexLocker(self.mutex):
                # Linux-specific system configuration
                self._configure_linux_system()
                
                # CRITICAL FIX: Force fresh QApplication like test script (don't reuse existing)
                existing_app = QApplication.instance()
                if existing_app:
                    logger.error("FORCING FRESH QAPPLICATION: Existing QApplication found - destroying to match test script")
                    existing_app.quit()
                    # Note: QApplication deletion is handled by Qt
                
                # Create fresh QApplication like test_video_debug.py
                logger.error("Creating fresh QApplication like test script")
                self.app = QApplication(sys.argv)
                self.app.setApplicationName("MbetterClient PyQt6")
                self.app.setApplicationVersion("2.0.0")
                self.app.setQuitOnLastWindowClosed(True)
                
                # CRITICAL: Check what attribute was removed here that might affect video
                # Was there originally: self.app.setAttribute(Qt.ApplicationAttribute.WA_OpaquePaintEvent, True)?
                logger.info("Skipping any application attributes that might interfere with video rendering")
                
                # Setup signal handling for proper shutdown
                self._setup_signal_handlers()
                
                # Linux-specific application settings
                self._configure_linux_app_settings()
                
                # Create player window with message bus reference
                self.window = PlayerWindow(self.settings, self.message_bus)
                
                # CRITICAL: Connect signal to slot for cross-thread video playback
                self.play_video_signal.connect(self.window.play_video, Qt.ConnectionType.QueuedConnection)
                logger.info("Connected play_video_signal to PlayerWindow.play_video with QueuedConnection")
                
                # CRITICAL FIX: Force proper window activation and focus for OpenGL context
                logger.error("APPLYING CRITICAL FIX: Forcing window focus for OpenGL context")
                self.window.setWindowOpacity(1.0)
                self.window.setAutoFillBackground(False)  # Let Qt handle background
                
                # CRITICAL: Force window to become active and gain focus for OpenGL context
                self.window.show()
                self.window.raise_()
                self.window.activateWindow()
                self.app.processEvents()  # Process pending events
                
                # Force application to recognize this as active window
                self.app.setActiveWindow(self.window)
                self.app.processEvents()
                
                # Debug the result immediately
                logger.error(f"After window focus fix - activeWindow: {self.app.activeWindow()}")
                logger.error(f"After window focus fix - focusWidget: {self.app.focusWidget()}")
                
                # Schedule additional window activation to ensure OpenGL context
                def ensure_focus():
                    if not self.app.activeWindow():
                        logger.error("Still no active window, forcing focus again")
                        self.window.activateWindow()
                        self.window.setFocus()
                        self.app.setActiveWindow(self.window)
                        self.app.processEvents()
                        logger.error(f"Second attempt - activeWindow: {self.app.activeWindow()}")
                
                QTimer.singleShot(100, ensure_focus)
                
                # Connect window signals
                self.window.position_changed.connect(self._on_position_changed)
                self.window.video_loaded.connect(self._on_video_loaded)
                
                # Subscribe to messages
                logger.info(f"Subscribing QtPlayer to message bus events...")
                self.message_bus.subscribe(self.name, MessageType.VIDEO_PLAY, self._handle_video_play)
                self.message_bus.subscribe(self.name, MessageType.VIDEO_PAUSE, self._handle_video_pause)
                self.message_bus.subscribe(self.name, MessageType.VIDEO_STOP, self._handle_video_stop)
                self.message_bus.subscribe(self.name, MessageType.VIDEO_SEEK, self._handle_video_seek)
                self.message_bus.subscribe(self.name, MessageType.VIDEO_VOLUME, self._handle_video_volume)
                self.message_bus.subscribe(self.name, MessageType.VIDEO_FULLSCREEN, self._handle_video_fullscreen)
                self.message_bus.subscribe(self.name, MessageType.TEMPLATE_CHANGE, self._handle_template_change)
                self.message_bus.subscribe(self.name, MessageType.OVERLAY_UPDATE, self._handle_overlay_update)
                self.message_bus.subscribe(self.name, MessageType.STATUS_REQUEST, self._handle_status_request)
                logger.info("QtPlayer subscriptions completed successfully")
                
                # Delay loading default overlay to allow JavaScript initialization
                QTimer.singleShot(2000, self._load_default_overlay)  # Wait 2 seconds
                
                logger.info("QtVideoPlayer (PyQt6) initialized successfully")
                return True
                
        except Exception as e:
            logger.error(f"QtVideoPlayer initialization failed: {e}")
            return False
    
    def _force_window_display(self):
        """Force window to display properly - aggressive approach"""
        try:
            if self.window:
                logger.debug("Forcing window display and repaint")
                self.window.show()
                self.window.raise_()
                self.window.activateWindow()
                self.window.repaint()
                
                # Force video widget to be visible and painted
                video_widget = self.window.video_widget
                if video_widget:
                    video_widget.show()
                    video_widget.repaint()
                    
                    # Force base video widget to be visible
                    base_video = video_widget.get_video_widget()
                    if base_video:
                        base_video.show()
                        base_video.repaint()
                
                logger.debug("Window display forced")
        except Exception as e:
            logger.error(f"Failed to force window display: {e}")
    
    def _configure_linux_system(self):
        """Configure Linux-specific system settings - TEMPORARILY DISABLED FOR TESTING"""
        import platform
        import os
        
        if platform.system() != 'Linux':
            return
            
        logger.info("TEMPORARILY DISABLING all Linux environment variables to test video display")
        return  # Skip all environment variable changes
        
        try:
            # TEMPORARILY DISABLED - ALL environment variables that might interfere with video
            linux_env_vars = {
                # 'QT_QPA_PLATFORM': 'xcb',
                # 'QT_AUTO_SCREEN_SCALE_FACTOR': '1',
                # 'QT_SCALE_FACTOR': '1',
                # 'QT_LOGGING_RULES': 'qt.qpa.xcb.info=false;qt.qpa.xcb.xcberror.warning=false',
                # 'QTWEBENGINE_CHROMIUM_FLAGS': '--no-sandbox --disable-gpu-sandbox',
                # 'QTWEBENGINE_DISABLE_SANDBOX': '1',
                # COMMENTED OUT: These OpenGL settings prevent video display
                # 'QT_OPENGL': 'software',  # Use software rendering instead of hardware - BLOCKS VIDEO
                # 'QT_QUICK_BACKEND': 'software',  # Software backend for Qt Quick - BLOCKS VIDEO
                # 'QT_XCB_GL_INTEGRATION': 'none',  # Disable XCB OpenGL integration - BLOCKS VIDEO
                # 'LIBGL_ALWAYS_SOFTWARE': '1',  # Force software OpenGL - BLOCKS VIDEO
                # 'QT_OPENGL_BUGLIST': 'disable',  # Disable OpenGL bug workarounds - BLOCKS VIDEO
            }
            
            for key, value in linux_env_vars.items():
                if key not in os.environ:
                    os.environ[key] = value
                    logger.debug(f"Set Linux env var: {key}={value}")
            
            # Handle headless/server environments
            if not os.environ.get('DISPLAY'):
                logger.warning("No DISPLAY environment variable - may be running headless")
                os.environ['QT_QPA_PLATFORM'] = 'offscreen'
            
        except Exception as e:
            logger.warning(f"Linux system configuration warning: {e}")
    
    def _configure_linux_app_settings(self):
        """Configure Linux-specific QApplication settings - DISABLED to match test script"""
        import platform
        
        if platform.system() != 'Linux' or not self.app:
            return
            
        try:
            # CRITICAL: Disable all Qt attributes to match simple test_video_debug.py approach
            logger.info("DISABLING all Qt application attributes to match test script - simple QApplication setup")
            # COMMENTED OUT: These attributes may interfere with video rendering
            # self.app.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps, True)
            # self.app.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling, True)
            # if hasattr(Qt.ApplicationAttribute, 'AA_X11InitThreads'):
            #     self.app.setAttribute(Qt.ApplicationAttribute.AA_X11InitThreads, True)
            
            logger.info("Qt attributes disabled - using simple QApplication like test script")
            
        except Exception as e:
            logger.warning(f"Linux application configuration warning: {e}")
    
    def _load_default_overlay(self):
        """Load default overlay display with enhanced WebChannel readiness checking"""
        try:
            default_data = {
                'title': 'MbetterClient PyQt6 Player',
                'subtitle': 'Ready for Content',
                'ticker': 'Welcome to MbetterClient • Professional Video Overlay System • Real-time Updates • Hardware Accelerated Playback',
                'showStats': False
            }
            
            if not self.window:
                logger.debug("Window not ready for overlay loading")
                return
                
            # Use the separate window overlay instead of video widget overlay
            if hasattr(self.window, 'window_overlay'):
                overlay_view = self.window.window_overlay
                
                # Check overlay type and handle accordingly
                if self.window._is_native_overlay(overlay_view):
                    # Native overlay is always ready
                    logger.debug("Native overlay ready, updating immediately...")
                    self._send_safe_overlay_update(overlay_view, default_data)
                elif isinstance(overlay_view, OverlayWebView):
                    # WebEngine overlay - enhanced readiness checking
                    if not self._is_webengine_ready(overlay_view):
                        logger.debug("WebEngine overlay not ready, retrying in 1 second...")
                        QTimer.singleShot(1000, self._load_default_overlay)
                        return
                    
                    # Additional delay before sending data to ensure JavaScript is fully initialized
                    logger.debug("WebEngine ready, scheduling overlay update...")
                    QTimer.singleShot(500, lambda: self._send_safe_overlay_update(overlay_view, default_data))
                else:
                    logger.warning(f"Unknown overlay type: {type(overlay_view)}")
            else:
                logger.warning("No window_overlay available for default overlay loading")
                
        except Exception as e:
            logger.error(f"Failed to load default overlay: {e}")
            # Retry on error with longer delay
            QTimer.singleShot(3000, self._load_default_overlay)
    
    def _is_webengine_ready(self, overlay_view):
        """Check if WebEngine overlay is fully ready for updates"""
        try:
            # Check if overlay_view is a QWebEngineView
            if not hasattr(overlay_view, 'page'):
                return False
                
            page = overlay_view.page()
            if not page:
                return False
            
            # Check if WebChannel exists
            if not page.webChannel():
                return False
                
            # Check if URL is loaded (not about:blank)
            current_url = overlay_view.url().toString()
            if current_url == 'about:blank' or not current_url:
                return False
            
            # Check if the overlay_channel object exists and is ready
            if not (hasattr(overlay_view, 'overlay_channel') and overlay_view.overlay_channel):
                return False
                
            return True
            
        except Exception as e:
            logger.debug(f"WebEngine readiness check failed: {e}")
            return False
    
    def _clean_overlay_data(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Clean overlay data by removing null/undefined values"""
        cleaned_data = {}
        
        # Define valid overlay properties and their expected types
        valid_properties = {
            'title': str,
            'subtitle': str,
            'ticker': str,
            'currentTime': str,
            'showStats': bool,
            'customCSS': str
        }
        
        for key, value in data.items():
            # Skip null, undefined, or empty string values
            if value is None or value == "":
                logger.debug(f"Skipping null/empty property '{key}' in overlay data")
                continue
            
            # Validate property type if it's a known property
            if key in valid_properties:
                expected_type = valid_properties[key]
                if not isinstance(value, expected_type):
                    logger.debug(f"Converting property '{key}' from {type(value)} to {expected_type}")
                    try:
                        # Convert to expected type
                        if expected_type == str:
                            cleaned_data[key] = str(value)
                        elif expected_type == bool:
                            cleaned_data[key] = bool(value)
                        else:
                            cleaned_data[key] = value
                    except (ValueError, TypeError):
                        logger.warning(f"Failed to convert property '{key}' to {expected_type}, skipping")
                        continue
                else:
                    cleaned_data[key] = value
            else:
                # Unknown property, pass through if not null
                cleaned_data[key] = value
        
        logger.debug(f"Cleaned overlay data: {cleaned_data}")
        return cleaned_data
    
    def _send_safe_overlay_update(self, overlay_view, data):
        """Send overlay update with additional safety checks"""
        try:
            # Clean data before sending to prevent null property issues
            cleaned_data = self._clean_overlay_data(data)
            if not cleaned_data:
                logger.debug("No valid data to send to overlay after cleaning")
                return
            
            if hasattr(self, 'window') and self.window:
                # Use the PlayerWindow's _update_overlay_safe method for consistency
                self.window._update_overlay_safe(overlay_view, cleaned_data)
                logger.info("Default WebEngine overlay loaded successfully")
            else:
                # Fallback to direct update if no window available
                if isinstance(overlay_view, OverlayWebView):
                    if self._is_webengine_ready(overlay_view):
                        overlay_view.update_overlay_data(cleaned_data)
                        logger.info("WebEngine overlay updated successfully")
                    else:
                        logger.debug("WebEngine overlay not ready, skipping update")
                else:
                    # Native overlay
                    overlay_view.update_overlay_data(cleaned_data)
                    logger.info("Native overlay updated successfully")
            
        except Exception as e:
            logger.error(f"Failed to send safe overlay update: {e}")
    
    def start_message_processing(self):
        """Start message processing using Qt timer on main thread"""
        if self.message_timer and self.message_timer.isActive():
            logger.warning("Message processing timer is already running")
            return
            
        logger.info(f"Starting message processing timer for component: {self.name}")
        logger.info(f"Message bus registered components: {list(self.message_bus._queues.keys()) if hasattr(self.message_bus, '_queues') else 'Unknown'}")
        
        # Create timer that runs on Qt main thread
        self.message_timer = QTimer()
        self.message_timer.timeout.connect(self._process_messages_main_thread)
        self.message_timer.start(10)  # Process messages every 10ms
        
        logger.info("QtPlayer message processing timer started successfully on Qt main thread")
    
    def stop_message_processing(self):
        """Stop message processing timer"""
        if self.message_timer and self.message_timer.isActive():
            self.message_timer.stop()
            logger.info("QtPlayer message processing timer stopped")
        else:
            logger.info("QtPlayer message processing timer already stopped")
    
    def _process_messages_main_thread(self):
        """Process messages from message bus on Qt main thread via QTimer"""
        try:
            # Process multiple messages per timer event for efficiency
            messages_processed = 0
            max_messages_per_cycle = 5
            
            while messages_processed < max_messages_per_cycle:
                try:
                    # Get message with no timeout (non-blocking)
                    message = self.message_bus.get_message(self.name, timeout=0.001)
                    if not message:
                        break
                        
                    messages_processed += 1
                    logger.info(f"QtPlayer RECEIVED message: {message.type.value} from {message.sender}")
                    logger.info(f"Message data: {message.data}")
                    
                    # Process message directly on main thread - no threading issues!
                    self._process_message(message)
                    
                except Exception as e:
                    logger.error(f"QtPlayer message processing error: {e}")
                    break
            
            # Send periodic progress updates if playing
            if (self.window and
                self.window.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState):
                self._send_progress_update()
                
        except Exception as e:
            logger.error(f"QtPlayer main thread message processing failed: {e}")
    
    def run(self):
        """Run the Qt event loop (this should be called on the main thread)"""
        try:
            logger.info("QtVideoPlayer starting Qt event loop")
            
            # Send ready status
            logger.info(f"QtVideoPlayer sending ready status from component: {self.name}")
            ready_message = MessageBuilder.system_status(
                sender=self.name,
                status="ready",
                details={"fullscreen": self.settings.fullscreen, "version": "PyQt6-2.0.0"}
            )
            publish_result = self.message_bus.publish(ready_message)
            logger.info(f"Ready status message publish result: {publish_result}")
            
            # Start message processing timer on main thread
            self.start_message_processing()
            
            # Run Qt event loop (this blocks until app quits)
            if self.app:
                return self.app.exec()
            else:
                logger.error("No QApplication instance available")
                return 1
                
        except Exception as e:
            logger.error(f"QtVideoPlayer run failed: {e}")
            return 1
    
    def _setup_signal_handlers(self):
        """Setup Qt-specific signal handlers - let main app handle Python signals"""
        try:
            # Only handle Qt application aboutToQuit signal
            # Let the main application handle SIGINT/SIGTERM to avoid conflicts
            if self.app:
                self.app.aboutToQuit.connect(self._handle_app_quit)
            
            logger.debug("Qt signal handlers setup completed")
            
        except Exception as e:
            logger.warning(f"Qt signal handler setup failed: {e}")
    
    def _handle_app_quit(self):
        """Handle Qt application quit signal"""
        logger.info("Qt application about to quit")
        
        # Ensure message processing timer is stopped
        self.stop_message_processing()
    
    def shutdown(self):
        """Shutdown video player"""
        try:
            logger.info("Shutting down QtVideoPlayer...")
            
            # Stop message processing timer
            self.stop_message_processing()
            
            with QMutexLocker(self.mutex):
                if self.window:
                    self.window.close()
                    self.window = None
                
                if self.app and not self.app.closingDown():
                    self.app.quit()
            
        except Exception as e:
            logger.error(f"QtVideoPlayer shutdown error: {e}")
    
    def _process_message(self, message: Message):
        """Process received message by routing to appropriate handlers"""
        try:
            logger.info(f"QtPlayer processing message type: {message.type.value}")
            
            # Route messages to appropriate handlers
            if message.type == MessageType.VIDEO_PLAY:
                logger.info("Calling _handle_video_play handler")
                self._handle_video_play(message)
            elif message.type == MessageType.VIDEO_PAUSE:
                logger.info("Calling _handle_video_pause handler")
                self._handle_video_pause(message)
            elif message.type == MessageType.VIDEO_STOP:
                logger.info("Calling _handle_video_stop handler")
                self._handle_video_stop(message)
            elif message.type == MessageType.VIDEO_SEEK:
                logger.info("Calling _handle_video_seek handler")
                self._handle_video_seek(message)
            elif message.type == MessageType.VIDEO_VOLUME:
                logger.info("Calling _handle_video_volume handler")
                self._handle_video_volume(message)
            elif message.type == MessageType.VIDEO_FULLSCREEN:
                logger.info("Calling _handle_video_fullscreen handler")
                self._handle_video_fullscreen(message)
            elif message.type == MessageType.TEMPLATE_CHANGE:
                logger.info("Calling _handle_template_change handler")
                self._handle_template_change(message)
            elif message.type == MessageType.OVERLAY_UPDATE:
                logger.info("Calling _handle_overlay_update handler")
                self._handle_overlay_update(message)
            elif message.type == MessageType.STATUS_REQUEST:
                logger.info("Calling _handle_status_request handler")
                self._handle_status_request(message)
            else:
                logger.warning(f"No handler for message type: {message.type.value}")
                
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
            import traceback
            logger.error(f"Full traceback: {traceback.format_exc()}")
    
    def _on_position_changed(self, position: int, duration: int):
        """Handle position changes from player window"""
        try:
            # Send progress update via message bus
            if duration > 0:
                percentage = (position / duration) * 100
                progress_message = MessageBuilder.video_progress(
                    sender=self.name,
                    position=position / 1000.0,  # Convert to seconds
                    duration=duration / 1000.0,  # Convert to seconds
                    percentage=percentage
                )
                self.message_bus.publish(progress_message, broadcast=True)
        except Exception as e:
            logger.error(f"Failed to send progress update: {e}")
    
    def _on_video_loaded(self, file_path: str):
        """Handle video loaded event"""
        try:
            logger.info(f"Video loaded: {file_path}")
            # Send video loaded status message
            loaded_message = MessageBuilder.system_status(
                sender=self.name,
                status="video_loaded",
                details={"file_path": file_path}
            )
            self.message_bus.publish(loaded_message, broadcast=True)
        except Exception as e:
            logger.error(f"Failed to handle video loaded event: {e}")
    
    def _send_progress_update(self):
        """Send video progress update"""
        try:
            if self.window and self.window.media_player.duration() > 0:
                position = self.window.media_player.position()
                duration = self.window.media_player.duration()
                percentage = (position / duration) * 100 if duration > 0 else 0
                
                progress_message = MessageBuilder.video_progress(
                    sender=self.name,
                    position=position / 1000.0,  # Convert to seconds
                    duration=duration / 1000.0,  # Convert to seconds
                    percentage=percentage
                )
                self.message_bus.publish(progress_message, broadcast=True)
                
        except Exception as e:
            logger.error(f"Failed to send progress update: {e}")
    
    # Message handlers for various video control commands
    def _handle_video_play(self, message: Message):
        """Handle video play message - now running on Qt main thread"""
        try:
            file_path = message.data.get("file_path")
            template_data = message.data.get("overlay_data", {})
            template_name = message.data.get("template")  # Extract template name from message
            
            logger.info(f"VIDEO_PLAY message received from {message.sender}")
            logger.info(f"File path: {file_path}")
            logger.info(f"Template name: {template_name}")
            logger.info(f"Overlay data: {template_data}")
            
            if not file_path:
                logger.error("No file path provided for video play")
                return
            
            if not self.window:
                logger.error("Qt player window not available")
                return
            
            # Log current overlay configuration
            overlay_type = "Native Qt Widgets" if self.settings.use_native_overlay else "QWebEngineView"
            logger.info(f"Qt Player overlay configuration: {overlay_type}")
            
            # PERFECT: Now running directly on Qt main thread - no threading issues!
            logger.info(f"Handler thread: {threading.current_thread().name}")
            logger.info(f"Handler is main thread: {threading.current_thread() is threading.main_thread()}")
            logger.info("CALLING play_video() DIRECTLY on Qt main thread - no cross-thread issues!")
            
            # Direct call - we're already on the main thread! Pass template name
            self.window.play_video(file_path, template_data, template_name)
            logger.info("play_video() called successfully on main thread")
            
        except Exception as e:
            logger.error(f"Failed to handle video play: {e}")
            import traceback
            logger.error(f"Full traceback: {traceback.format_exc()}")
    
    def _handle_video_pause(self, message: Message):
        """Handle video pause message - now running on Qt main thread"""
        try:
            if self.window:
                # Direct call - we're already on the main thread!
                self.window.media_player.pause()
        except Exception as e:
            logger.error(f"Failed to handle video pause: {e}")
    
    def _handle_video_stop(self, message: Message):
        """Handle video stop message - now running on Qt main thread"""
        try:
            if self.window:
                # Direct call - we're already on the main thread!
                self.window.stop_playback()
        except Exception as e:
            logger.error(f"Failed to handle video stop: {e}")
    
    def _handle_video_seek(self, message: Message):
        """Handle video seek message - now running on Qt main thread"""
        try:
            position = message.data.get("position", 0)
            if self.window:
                # Direct call - we're already on the main thread!
                self._do_video_seek(position)
        except Exception as e:
            logger.error(f"Failed to handle video seek: {e}")
    
    def _do_video_seek(self, position):
        """Execute video seek on main thread"""
        try:
            duration = self.window.media_player.duration()
            if duration > 0:
                percentage = int(position * 100 / duration)
                self.window.seek_to_position(percentage)
        except Exception as e:
            logger.error(f"Failed to execute video seek: {e}")
    
    def _handle_video_volume(self, message: Message):
        """Handle video volume message - now running on Qt main thread"""
        try:
            volume = message.data.get("volume", 100)
            if self.window:
                # Direct call - we're already on the main thread!
                self.window.set_volume(volume)
        except Exception as e:
            logger.error(f"Failed to handle video volume: {e}")
    
    def _handle_video_fullscreen(self, message: Message):
        """Handle video fullscreen message - now running on Qt main thread"""
        try:
            fullscreen = message.data.get("fullscreen", True)
            if self.window:
                # Direct call - we're already on the main thread!
                if fullscreen:
                    self.window.showFullScreen()
                else:
                    self.window.showNormal()
        except Exception as e:
            logger.error(f"Failed to handle video fullscreen: {e}")
    
    def _handle_template_change(self, message: Message):
        """Handle template change message - now running on Qt main thread"""
        try:
            template_name = message.data.get("template_name", "")
            template_data = message.data.get("template_data", {})
            reload_template = template_data.get("reload_template", False)
            load_specific_template = template_data.get("load_specific_template", "")
            
            if self.window and hasattr(self.window, 'window_overlay'):
                overlay_view = self.window.window_overlay
                
                # Load specific template if requested and using WebEngine overlay
                if load_specific_template and isinstance(overlay_view, OverlayWebView):
                    logger.info(f"Loading specific template: {load_specific_template}")
                    overlay_view.load_template(load_specific_template)
                # Otherwise reload current template if requested and using WebEngine overlay
                elif reload_template and isinstance(overlay_view, OverlayWebView):
                    logger.info("Reloading current template due to template_change message")
                    overlay_view.reload_current_template()
                
                # Update overlay data if provided (excluding template control flags)
                if template_data:
                    # Remove template control flags from data to be sent to overlay
                    data_to_send = {k: v for k, v in template_data.items()
                                  if k not in ['reload_template', 'load_specific_template']}
                    
                    if data_to_send:
                        # Validate and clean template_data before sending to overlay
                        cleaned_data = self._clean_overlay_data(data_to_send)
                        if cleaned_data:  # Only send if we have valid data after cleaning
                            self.window._update_overlay_safe(overlay_view, cleaned_data)
                        else:
                            logger.debug("Template data contained only null/undefined values, skipping update")
                
        except Exception as e:
            logger.error(f"Failed to handle template change: {e}")
    
    def _handle_overlay_update(self, message: Message):
        """Handle overlay update message - now running on Qt main thread"""
        try:
            overlay_data = message.data.get("overlay_data", {})
            
            if self.window and overlay_data and hasattr(self.window, 'window_overlay'):
                # Validate and clean overlay_data before sending to overlay
                cleaned_data = self._clean_overlay_data(overlay_data)
                if cleaned_data:  # Only send if we have valid data after cleaning
                    overlay_view = self.window.window_overlay
                    self.window._update_overlay_safe(overlay_view, cleaned_data)
                else:
                    logger.debug("Overlay data contained only null/undefined values, skipping update")
                
        except Exception as e:
            logger.error(f"Failed to handle overlay update: {e}")
    
    def _handle_status_request(self, message: Message):
        """Handle status request message - now running on Qt main thread"""
        try:
            if self.window:
                # Direct call - we're already on the main thread!
                self._do_status_request(message)
        except Exception as e:
            logger.error(f"Failed to handle status request: {e}")
    
    def _do_status_request(self, message: Message):
        """Execute status request on main thread"""
        try:
            is_playing = (self.window.media_player.playbackState() ==
                        QMediaPlayer.PlaybackState.PlayingState)
            position = self.window.media_player.position() / 1000.0  # seconds
            duration = self.window.media_player.duration() / 1000.0  # seconds
            volume = self.window.audio_output.volume() * 100
            muted = self.window.audio_output.isMuted()
            fullscreen = self.window.isFullScreen()
            
            status_response = MessageBuilder.system_status(
                sender=self.name,
                status="status_response",
                details={
                    "player_status": "playing" if is_playing else "paused",
                    "position": position,
                    "duration": duration,
                    "volume": volume,
                    "muted": muted,
                    "fullscreen": fullscreen
                }
            )
            # Send response back to requester
            status_response.recipient = message.sender
            self.message_bus.publish(status_response)
        except Exception as e:
            logger.error(f"Failed to execute status request: {e}")
