"""
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
import os
from pathlib import Path
from typing import Optional, Dict, Any, List

# Suppress Chromium sandbox warnings when running as root - MUST be set before Qt imports
if os.geteuid() == 0:  # Running as root
    os.environ['QTWEBENGINE_DISABLE_SANDBOX'] = '1'
    print("Qt WebEngine sandbox disabled for root user")

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
        data_keys = list(cleaned_data.keys()) if isinstance(cleaned_data, dict) else []
        logger.debug(f"OverlayWebChannel sending to JavaScript: {len(cleaned_data)} items with keys: {data_keys}")
        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)
                data_keys = list(cleaned_data.keys()) if isinstance(cleaned_data, dict) else []
                logger.debug(f"Signal emitted successfully with {len(cleaned_data)} data items: {data_keys}")
            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, debug_overlay=False):
        super().__init__(parent)
        self.debug_overlay = debug_overlay
        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")

        # Detect Mesa software rendering
        import os
        is_mesa = os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or \
                  os.environ.get('MESA_GL_VERSION_OVERRIDE') is not None

        # Set transparent background on the web page
        page = self.page()

        if is_mesa:
            # Mesa-specific transparency settings
            logger.debug("Mesa software rendering detected - applying Mesa transparency fixes")
            page.setBackgroundColor(QColor(0, 0, 0, 1))  # Semi-transparent for Mesa
            # Use CSS-based transparency for Mesa
            page.runJavaScript("""
                document.body.style.backgroundColor = 'rgba(0, 0, 0, 0.01)';
                document.documentElement.style.backgroundColor = 'rgba(0, 0, 0, 0.01)';
            """)
        else:
            # Standard hardware transparency
            page.setBackgroundColor(QColor(0, 0, 0, 0))  # Fully transparent

        # Widget should be visible but allow transparency
        if is_mesa:
            self.setStyleSheet("""
                QWebEngineView {
                    border: none;
                    background: rgba(0, 0, 0, 0.01);
                }
            """)
        else:
            self.setStyleSheet("""
                QWebEngineView {
                    border: none;
                    background: transparent;
                }
            """)

        logger.debug(f"OverlayWebView setup completed - Mesa: {is_mesa}, transparency configured")
        
        # 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:
            # Register the custom scheme before installing the handler
            from PyQt6.QtWebEngineCore import QWebEngineUrlScheme
            overlay_scheme = QWebEngineUrlScheme(b"overlay")
            overlay_scheme.setSyntax(QWebEngineUrlScheme.Syntax.HostAndPort)
            overlay_scheme.setDefaultPort(0)
            overlay_scheme.setFlags(QWebEngineUrlScheme.Flag.SecureScheme |
                                   QWebEngineUrlScheme.Flag.LocalScheme |
                                   QWebEngineUrlScheme.Flag.LocalAccessAllowed)
            QWebEngineUrlScheme.registerScheme(overlay_scheme)

            logger.info("Custom overlay:// URL scheme registered successfully")

            # 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 self.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Starting template load - {template_name}")
                logger.debug(f"GREEN SCREEN DEBUG: Current page URL before load: {self.url().toString()}")
                logger.debug(f"GREEN SCREEN DEBUG: WebEngine view visible: {self.isVisible()}")
            
            # CRITICAL FIX: Store visibility state before template load
            was_visible = self.isVisible()
            
            # 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():
                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: About to load template file: {template_path}")
                    logger.debug(f"GREEN SCREEN DEBUG: Template source: {template_source}")

                    # Check WebEngine state before load
                    page = self.page()
                    if page:
                        logger.debug(f"GREEN SCREEN DEBUG: Page background color before load: {page.backgroundColor()}")

                self.load(QUrl.fromLocalFile(str(template_path)))
                self.current_template = template_name

                # CRITICAL FIX: Force visibility recovery after template load
                if was_visible and not self.isVisible():
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN FIX: Recovering overlay visibility after template load")
                    self.show()
                    self.raise_()

                # CRITICAL FIX: Schedule additional visibility check after page load
                from PyQt6.QtCore import QTimer
                QTimer.singleShot(100, lambda: self._ensure_overlay_visibility_post_load(was_visible))

                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Template load initiated - {template_path}")
                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:
            if self.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Template load failed: {e}")
            logger.error(f"Failed to load template {template_name}: {e}")
            self._load_fallback_overlay()
    
    def _ensure_overlay_visibility_post_load(self, should_be_visible: bool):
        """Ensure overlay maintains proper visibility after template load"""
        try:
            if should_be_visible and not self.isVisible():
                logger.debug(f"GREEN SCREEN FIX: Overlay lost visibility after template load, forcing recovery")
                self.show()
                self.raise_()
                logger.debug(f"GREEN SCREEN FIX: Overlay visibility recovered")
            
            # Check parent overlay window visibility as well
            parent = self.parent()
            if parent and should_be_visible and not parent.isVisible():
                logger.debug(f"GREEN SCREEN FIX: Parent overlay window lost visibility, forcing recovery")
                parent.show()
                parent.raise_()
                logger.debug(f"GREEN SCREEN FIX: Parent overlay window visibility recovered")
                
            # CRITICAL FIX: Protect video rendering context during template operations
            self._protect_video_context_during_template_load()
                
        except Exception as e:
            logger.error(f"GREEN SCREEN FIX: Failed to ensure overlay visibility: {e}")
    
    def _protect_video_context_during_template_load(self):
        """Protect video rendering context during template loading operations"""
        try:
            if hasattr(self, 'parent') and self.parent():
                parent_window = self.parent()
                if hasattr(parent_window, 'media_player') and parent_window.media_player:
                    logger.debug(f"GREEN SCREEN FIX: Protecting video context during template load")
                    media_player = parent_window.media_player
                    
                    # Store current video output
                    current_output = media_player.videoOutput()
                    if current_output:
                        logger.debug(f"GREEN SCREEN FIX: Video output preserved during template load")
                    
                    # Force repaint of video widget
                    if hasattr(parent_window, 'video_widget') and parent_window.video_widget:
                        video_widget = parent_window.video_widget.get_video_widget()
                        if video_widget:
                            video_widget.repaint()
                            video_widget.update()
                            logger.debug(f"GREEN SCREEN FIX: Video widget repainted during template load")
        except Exception as e:
            logger.debug(f"GREEN SCREEN FIX: Failed to protect video context: {e}")
    
    def reload_current_template(self):
        """Reload the current template"""
        if self.debug_overlay:
            logger.debug(f"GREEN SCREEN DEBUG: Reloading current template - {self.current_template}")
            logger.debug(f"GREEN SCREEN DEBUG: WebEngine state before reload - visible: {self.isVisible()}")
        self.load_template(self.current_template)
        if self.debug_overlay:
            logger.debug(f"GREEN SCREEN DEBUG: Current template reload initiated")
    
    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, debug_overlay: bool = False):
        super().__init__()
        self.settings = settings
        self.debug_overlay = debug_overlay
        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 _get_web_server_base_url(self) -> str:
        """Get the web server base URL for API requests"""
        try:
            # Default web server configuration - matches main.py defaults
            host = "127.0.0.1"  # Default host
            port = 5001  # Default port
            
            # Try to get web server settings if available
            if hasattr(self, '_message_bus') and self._message_bus:
                # Check if we can get web server info from message bus or settings
                # For now, use defaults since we don't have direct access to web settings
                pass
            
            # Construct base URL
            base_url = f"http://{host}:{port}"
            logger.debug(f"Web server base URL determined as: {base_url}")
            return base_url
            
        except Exception as e:
            logger.error(f"Failed to determine web server base URL: {e}")
            # Return default fallback
            return "http://127.0.0.1:5001"
    
    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
        )

        # Mesa-specific transparency handling
        import os
        is_mesa = os.environ.get('LIBGL_ALWAYS_SOFTWARE') == '1' or \
                  os.environ.get('MESA_GL_VERSION_OVERRIDE') is not None

        if is_mesa:
            # Mesa compatibility - use composition-based transparency
            self.overlay_window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, False)
            self.overlay_window.setStyleSheet("background: rgba(0, 0, 0, 0.01);")
            logger.debug("Mesa overlay window configured with composition transparency")
        else:
            # Standard hardware transparency
            self.overlay_window.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground, True)
            self.overlay_window.setStyleSheet("background: transparent;")
            logger.debug("Hardware overlay window configured with native transparency")

        # 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:
            # Pass debug_overlay setting to OverlayWebView
            debug_overlay = getattr(self, 'debug_overlay', False)
            self.window_overlay = OverlayWebView(self.overlay_window, debug_overlay=debug_overlay)
            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:
                # Check video state during overlay positioning
                if hasattr(self, 'media_player'):
                    video_state = self.media_player.playbackState()
                    logger.debug(f"GREEN SCREEN DEBUG: Video state during overlay sync: {video_state}")
                
                # Get main window geometry
                main_geo = self.geometry()
                logger.debug(f"Main window geometry: {main_geo}")
                logger.debug(f"GREEN SCREEN DEBUG: Syncing overlay position - main geometry: {main_geo}")
                
                # CRITICAL FIX: Store overlay visibility state before sync
                overlay_was_visible = self.overlay_window.isVisible()
                
                # Check overlay window state before positioning
                logger.debug(f"GREEN SCREEN DEBUG: Overlay window before sync - visible: {overlay_was_visible}")
                logger.debug(f"GREEN SCREEN DEBUG: Overlay window before sync - geometry: {self.overlay_window.geometry()}")
                
                # Position overlay window exactly over main window
                self.overlay_window.setGeometry(main_geo)
                self.overlay_window.show()
                self.overlay_window.raise_()
                
                # CRITICAL FIX: Ensure WebEngine overlay within window maintains visibility
                if hasattr(self, 'window_overlay') and hasattr(self.window_overlay, 'isVisible'):
                    webengine_visible = self.window_overlay.isVisible()
                    if not webengine_visible:
                        logger.error(f"GREEN SCREEN FIX: WebEngine overlay lost visibility during sync, recovering")
                        self.window_overlay.show()
                        self.window_overlay.raise_()
                        logger.error(f"GREEN SCREEN FIX: WebEngine overlay visibility recovered during sync")
                
                if self.debug_overlay:
                    logger.debug(f"Overlay window positioned at: {self.overlay_window.geometry()}")
                    logger.debug(f"GREEN SCREEN DEBUG: Overlay window after sync - geometry: {self.overlay_window.geometry()}")

        except Exception as e:
            if self.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Overlay sync failed: {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)
        
        # Loop control state
        self.loop_enabled = False
        self.infinite_loop = False
        self.loop_count = 0
        self.current_loop_iteration = 0
        self.current_file_path = None

        # Match video tracking
        self.is_playing_match_video = False
        self.current_match_id = None
        self.current_match_video_filename = None
        self.current_fixture_id = None

        # Template rotation state
        self.template_sequence = []
        self.rotating_time = '05:00'
        self.current_template_index = 0
        self.template_rotation_timer = None

        # 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

        # Template rotation timer (initially disabled)
        self.template_rotation_timer = QTimer()
        self.template_rotation_timer.timeout.connect(self._rotate_template)
        self.template_rotation_timer.setSingleShot(False)  # Repeat timer

        # Mouse tracking for window interactions (no controls to show/hide)
        self.setMouseTracking(True)

    def _start_template_rotation(self):
        """Start the template rotation timer"""
        try:
            if not self.template_sequence or len(self.template_sequence) <= 1:
                logger.debug("Template rotation not needed - insufficient templates")
                return

            # Parse rotating time (format: MM:SS)
            try:
                minutes, seconds = map(int, self.rotating_time.split(':'))
                rotation_interval_ms = (minutes * 60 + seconds) * 1000  # Convert to milliseconds
            except (ValueError, AttributeError):
                logger.warning(f"Invalid rotating_time format '{self.rotating_time}', using default 5 minutes")
                rotation_interval_ms = 5 * 60 * 1000  # Default 5 minutes

            logger.info(f"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})")

            # Stop any existing timer
            if self.template_rotation_timer and self.template_rotation_timer.isActive():
                self.template_rotation_timer.stop()

            # Start the rotation timer
            self.template_rotation_timer.start(rotation_interval_ms)
            self.current_template_index = 0  # Reset to first template

        except Exception as e:
            logger.error(f"Failed to start template rotation: {e}")

    def _rotate_template(self):
        """Rotate to the next template in the sequence"""
        try:
            if not self.template_sequence or len(self.template_sequence) == 0:
                logger.debug("No template sequence available for rotation")
                return

            # Move to next template
            self.current_template_index = (self.current_template_index + 1) % len(self.template_sequence)
            next_template = self.template_sequence[self.current_template_index]['name']

            logger.info(f"Rotating to template: {next_template} (index {self.current_template_index})")

            # Load the new template
            if hasattr(self, 'window_overlay') and isinstance(self.window_overlay, OverlayWebView):
                self.window_overlay.load_template(next_template)
                logger.info(f"Template rotated to: {next_template}")
            else:
                logger.warning("No WebEngine overlay available for template rotation")

        except Exception as e:
            logger.error(f"Failed to rotate template: {e}")

    
    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, loop_data: Dict[str, Any] = None):
        """Play video file with optional overlay data, template, and loop control"""
        try:
            logger.info(f"PlayerWindow.play_video() called with: {file_path}")
            logger.info(f"Loop data: {loop_data}")
            logger.info(f"Media player state before play: {self.media_player.playbackState()}")
            logger.info(f"Media player error state: {self.media_player.error()}")

            # Process loop control parameters
            if loop_data:
                self.loop_enabled = loop_data.get('loop_mode', False) or loop_data.get('infinite_loop', False) or loop_data.get('continuous_playback', False)
                self.infinite_loop = loop_data.get('infinite_loop', False) or loop_data.get('continuous_playback', False)
                self.loop_count = loop_data.get('loop_count', 0)

                # Handle template rotation for intro videos
                self.template_sequence = loop_data.get('template_sequence', [])
                self.rotating_time = loop_data.get('rotating_time', '05:00')
                self.current_template_index = 0

                if self.template_sequence:
                    logger.info(f"TEMPLATE ROTATION ENABLED: {len(self.template_sequence)} templates, rotating every {self.rotating_time}")

                if self.infinite_loop or self.loop_count == -1:
                    self.infinite_loop = True
                    logger.info("INFINITE LOOP MODE ENABLED")
                elif self.loop_count > 0:
                    logger.info(f"FINITE LOOP MODE ENABLED - {self.loop_count} iterations")

                self.current_loop_iteration = 0
            else:
                # No loop data - disable looping and template rotation
                self.loop_enabled = False
                self.infinite_loop = False
                self.loop_count = 0
                self.current_loop_iteration = 0
                self.template_sequence = []
                self.rotating_time = '05:00'
                self.current_template_index = 0
            
            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()}")
                
                # Store current file path for loop functionality
                self.current_file_path = str(absolute_path)
                
                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()}")
                
                # CRITICAL FIX: Protect video rendering context during template operations
                logger.debug(f"GREEN SCREEN FIX: Protecting video rendering context during template load")
                
                # Store video widget state before template operations
                video_widget = None
                if hasattr(self.video_widget, 'get_video_widget'):
                    video_widget = self.video_widget.get_video_widget()
                
                if video_widget:
                    logger.debug(f"GREEN SCREEN FIX: Video widget state before template load - visible: {video_widget.isVisible()}")
                
                # 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:
                        logger.debug(f"GREEN SCREEN FIX: Loading template while protecting video context: {template_name}")
                        self.window_overlay.load_template(template_name)
                        logger.info(f"Loaded template '{template_name}' for video playback")
                    else:
                        logger.debug(f"GREEN SCREEN FIX: Reloading template while protecting video context")
                        self.window_overlay.reload_current_template()
                        logger.info("Reloaded current overlay template for video playback")
                
                # CRITICAL FIX: Force video widget refresh after template operations
                if video_widget:
                    logger.debug(f"GREEN SCREEN FIX: Forcing video widget refresh after template load")
                    video_widget.repaint()
                    video_widget.update()
                    logger.debug(f"GREEN SCREEN FIX: Video widget state after template load - visible: {video_widget.isVisible()}")
                
                # 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',
                    'webServerBaseUrl': self._get_web_server_base_url()
                })
                
                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))
                
                # Start template rotation timer if enabled
                if self.template_sequence and len(self.template_sequence) > 1:
                    self._start_template_rotation()

                if self.settings.auto_play:
                    self.media_player.play()

                # Ensure proper window focus for video playback
                app = QApplication.instance()
                if app:
                    self.show()
                    self.raise_()
                    self.activateWindow()
                    app.processEvents()
                    app.setActiveWindow(self)
                    logger.debug(f"Window focus applied for video playback - activeWindow: {app.activeWindow()}")
                else:
                    logger.warning("No QApplication instance found for window focus")

                # 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()

            # Stop template rotation timer
            if self.template_rotation_timer and self.template_rotation_timer.isActive():
                self.template_rotation_timer.stop()
                logger.debug("Template rotation timer stopped")
    
    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 and loop control"""
        logger.debug(f"LOOP DEBUG: Media status changed to: {status} ({status.name if hasattr(status, 'name') else 'unknown'})")

        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)

        elif status == QMediaPlayer.MediaStatus.EndOfMedia:
            # Handle end of media for loop functionality and match video completion
            logger.debug(f"LOOP DEBUG: END OF MEDIA DETECTED!")
            logger.debug(f"LOOP DEBUG: Loop enabled: {self.loop_enabled}")
            logger.debug(f"LOOP DEBUG: Infinite loop: {self.infinite_loop}")
            logger.debug(f"LOOP DEBUG: Current iteration: {self.current_loop_iteration}")
            logger.debug(f"LOOP DEBUG: Loop count: {self.loop_count}")
            logger.debug(f"MATCH DEBUG: Is playing match video: {self.is_playing_match_video}")

            # Check if this is the end of a match video
            if self.is_playing_match_video:
                logger.info(f"MATCH DEBUG: Match video ended - sending PLAY_VIDEO_MATCH_DONE")
                self._send_match_video_done_message()
                # Reset match video tracking
                self.is_playing_match_video = False
                self.current_match_id = None
                self.current_match_video_filename = None
                self.current_fixture_id = None
                return

            # Handle loop functionality for intro videos
            if self.loop_enabled:
                logger.debug(f"LOOP DEBUG: Processing loop restart logic...")
                if self.infinite_loop:
                    # Infinite loop - restart immediately
                    self.current_loop_iteration += 1
                    logger.debug(f"LOOP DEBUG: INFINITE LOOP - Starting iteration {self.current_loop_iteration}")
                    self._restart_video()
                elif self.loop_count > 0 and self.current_loop_iteration < self.loop_count:
                    # Finite loop - check if we should continue
                    self.current_loop_iteration += 1
                    logger.debug(f"LOOP DEBUG: FINITE LOOP - Starting iteration {self.current_loop_iteration}/{self.loop_count}")
                    self._restart_video()
                else:
                    logger.debug(f"LOOP DEBUG: Loop completed after {self.current_loop_iteration} iterations - stopping playback")
                    self.loop_enabled = False
            else:
                logger.debug(f"LOOP DEBUG: No loop enabled - playback finished")
    
    def _restart_video(self):
        """Restart video playback for looping"""
        try:
            logger.debug(f"LOOP DEBUG: _restart_video called")
            logger.debug(f"LOOP DEBUG: Current file path: {self.current_file_path}")
            logger.debug(f"LOOP DEBUG: Media player state: {self.media_player.playbackState()}")
            
            if self.current_file_path:
                logger.debug(f"LOOP DEBUG: Restarting video for loop: {self.current_file_path}")
                url = QUrl.fromLocalFile(self.current_file_path)
                logger.debug(f"LOOP DEBUG: Created QUrl: {url.toString()}")
                
                self.media_player.setSource(url)
                logger.debug(f"LOOP DEBUG: Set media player source")
                
                self.media_player.play()
                logger.debug(f"LOOP DEBUG: Called media player play()")
                logger.debug(f"LOOP DEBUG: Media player state after play(): {self.media_player.playbackState()}")
                
                # Update overlay to show loop status
                if hasattr(self, 'window_overlay'):
                    overlay_view = self.window_overlay
                    loop_info = f"Loop {self.current_loop_iteration}"
                    if self.infinite_loop:
                        loop_info += " (∞)"
                    else:
                        loop_info += f"/{self.loop_count}"
                    
                    status_data = {'subtitle': f'Playing - {loop_info}'}
                    self._update_overlay_safe(overlay_view, status_data)
                    logger.debug(f"LOOP DEBUG: Updated overlay with loop info: {loop_info}")
            else:
                logger.error("LOOP DEBUG: No current file path available for restart")
        except Exception as e:
            logger.debug(f"LOOP DEBUG: Failed to restart video for loop: {e}")
    
    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,
                    'webServerBaseUrl': self._get_web_server_base_url()
                }
                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

            # Stop template rotation timer
            if self.template_rotation_timer and self.template_rotation_timer.isActive():
                self.template_rotation_timer.stop()
                logger.debug("Template rotation timer stopped on window close")

            # 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
        
        data_keys = list(cleaned_data.keys()) if isinstance(cleaned_data, dict) else []
        logger.debug(f"Cleaned overlay data: {len(cleaned_data)} items with keys: {data_keys}")
        return cleaned_data
    
    def _update_overlay_safe(self, overlay_view, data):
        """Update overlay data safely, handling both native and WebEngine overlays"""
        try:
            # Check video state during overlay update
            if hasattr(self, 'media_player') and self.debug_overlay:
                video_state = self.media_player.playbackState()
                logger.debug(f"GREEN SCREEN DEBUG: Video state during overlay update: {video_state}")
                logger.debug(f"GREEN SCREEN DEBUG: Video position during overlay update: {self.media_player.position()}")

            # 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.debug_overlay:
                data_keys = list(cleaned_data.keys()) if isinstance(cleaned_data, dict) else []
                logger.debug(f"GREEN SCREEN DEBUG: About to update overlay with {len(cleaned_data)} data items: {data_keys}")
            
            if self._is_native_overlay(overlay_view):
                # Native overlay - always ready, update immediately
                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Updating native overlay")
                overlay_view.update_overlay_data(cleaned_data)
                if self.debug_overlay:
                    logger.debug("Native overlay updated successfully")
                    logger.debug(f"GREEN SCREEN DEBUG: Native overlay update completed")
                return True
            elif isinstance(overlay_view, OverlayWebView):
                # WebEngine overlay - check readiness first
                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Checking WebEngine overlay readiness")
                    logger.debug(f"GREEN SCREEN DEBUG: WebEngine URL: {overlay_view.url().toString()}")
                    logger.debug(f"GREEN SCREEN DEBUG: WebEngine visible: {overlay_view.isVisible()}")

                # CRITICAL FIX: Ensure WebEngine overlay visibility before update
                if not overlay_view.isVisible():
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN FIX: WebEngine overlay not visible, forcing visibility recovery")
                    overlay_view.show()
                    overlay_view.raise_()
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN FIX: WebEngine overlay visibility forced during update")

                    # Also ensure parent overlay window is visible
                    if hasattr(self, 'overlay_window') and self.overlay_window and not self.overlay_window.isVisible():
                        if self.debug_overlay:
                            logger.debug(f"GREEN SCREEN FIX: Parent overlay window not visible, forcing visibility")
                        self.overlay_window.show()
                        self.overlay_window.raise_()
                        if self.debug_overlay:
                            logger.debug(f"GREEN SCREEN FIX: Parent overlay window visibility forced")

                if self._is_webengine_ready(overlay_view):
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: WebEngine ready, updating overlay")
                    overlay_view.update_overlay_data(cleaned_data)
                    if self.debug_overlay:
                        logger.debug("WebEngine overlay updated successfully")
                        logger.debug(f"GREEN SCREEN DEBUG: WebEngine overlay update completed")
                    return True
                else:
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: WebEngine not ready, skipping update")
                        logger.debug("WebEngine overlay not ready, skipping update")
                    return False
            else:
                logger.warning(f"Unknown overlay type: {type(overlay_view)}")
                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Unknown overlay type: {type(overlay_view)}")
                return False
        except Exception as e:
            if self.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Overlay update failed: {e}")
            logger.error(f"Failed to update overlay safely: {e}")
            return False
    
    
    def _protect_video_context_after_template_load(self):
        """Protect video rendering context after template loading operations"""
        try:
            logger.debug(f"GREEN SCREEN FIX: Protecting video context after template load")
            
            # Force repaint of video widget
            if hasattr(self, 'video_widget') and hasattr(self.video_widget, 'get_video_widget'):
                video_widget = self.video_widget.get_video_widget()
                if video_widget:
                    video_widget.repaint()
                    video_widget.update()
                    logger.debug(f"GREEN SCREEN FIX: Video widget repainted after template load")
            
            # Force media player to refresh video output
            if hasattr(self, 'media_player'):
                logger.debug(f"GREEN SCREEN FIX: Refreshing media player video output after template load")
                current_output = self.media_player.videoOutput()
                if current_output:
                    # Force video output refresh by briefly setting to None and back
                    self.media_player.setVideoOutput(None)
                    self.media_player.setVideoOutput(current_output)
                    logger.debug(f"GREEN SCREEN FIX: Media player video output refreshed after template load")
        except Exception as e:
            logger.debug(f"GREEN SCREEN FIX: Failed to protect video context after template load: {e}")
    
    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, debug_player: bool = False, debug_overlay: bool = False):
        super().__init__()
        self.name = "qt_player"
        self.message_bus = message_bus
        self.settings = settings
        self.debug_player = debug_player
        self.debug_overlay = debug_overlay
        self.app: Optional[QApplication] = None
        self.window: Optional[PlayerWindow] = None
        self.mutex = QMutex()

        # Set web dashboard URL for API calls
        self.web_dashboard_url = "http://localhost:5000"  # Default web dashboard URL

        # 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 _get_web_server_base_url(self) -> str:
        """Get the web server base URL for API requests"""
        try:
            # Default web server configuration - matches main.py defaults
            host = "127.0.0.1"  # Default host
            port = 5001  # Default port
            
            # Try to get web server settings if available
            if hasattr(self, '_message_bus') and self._message_bus:
                # Check if we can get web server info from message bus or settings
                # For now, use defaults since we don't have direct access to web settings
                pass
            
            # Construct base URL
            base_url = f"http://{host}:{port}"
            logger.debug(f"Web server base URL determined as: {base_url}")
            return base_url
            
        except Exception as e:
            logger.error(f"Failed to determine web server base URL: {e}")
            # Return default fallback
            return "http://127.0.0.1:5001"
    
    def initialize(self) -> bool:
        """Initialize PyQt6 application and components"""
        try:
            with QMutexLocker(self.mutex):
                # Linux-specific system configuration
                self._configure_linux_system()
                
                # Create QApplication if needed
                existing_app = QApplication.instance()
                if existing_app:
                    logger.debug("Using existing QApplication instance")
                    self.app = existing_app
                else:
                    logger.info("Creating new QApplication")
                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 and debug settings
                self.window = PlayerWindow(self.settings, self.message_bus, debug_overlay=self.debug_overlay)
                
                # 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")
                
                # Apply window focus for proper OpenGL context
                logger.debug("Applying 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()
                
                # Verify window focus was applied
                logger.debug(f"Window focus applied - activeWindow: {self.app.activeWindow()}")
                
                # Schedule additional window activation to ensure proper focus
                def ensure_focus():
                    if not self.app.activeWindow():
                        logger.debug("Applying additional window focus")
                        self.window.activateWindow()
                        self.window.setFocus()
                        self.app.setActiveWindow(self.window)
                        self.app.processEvents()
                        logger.debug(f"Additional focus applied - 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)
                self.message_bus.subscribe(self.name, MessageType.START_INTRO, self._handle_start_intro)
                self.message_bus.subscribe(self.name, MessageType.PLAY_VIDEO_MATCH, self._handle_play_video_match)
                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
            
        # Linux environment variables are properly configured for Qt xcb platform support
        
        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,
                'webServerBaseUrl': self._get_web_server_base_url()
            }
            
            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
        
        data_keys = list(cleaned_data.keys()) if isinstance(cleaned_data, dict) else []
        logger.debug(f"Cleaned overlay data: {len(cleaned_data)} items with keys: {data_keys}")
        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
                    if self.debug_player:
                        logger.info(f"QtPlayer RECEIVED message: {message.type.value} from {message.sender}")
                        # Don't log full message data to avoid cluttering logs with HTML content
                        logger.debug(f"Message data keys: {list(message.data.keys()) if isinstance(message.data, dict) else 'non-dict 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:
            if self.debug_player:
                logger.info(f"QtPlayer processing message type: {message.type.value}")

            # Route messages to appropriate handlers
            if message.type == MessageType.VIDEO_PLAY:
                if self.debug_player:
                    logger.info("Calling _handle_video_play handler")
                self._handle_video_play(message)
            elif message.type == MessageType.VIDEO_PAUSE:
                if self.debug_player:
                    logger.info("Calling _handle_video_pause handler")
                self._handle_video_pause(message)
            elif message.type == MessageType.VIDEO_STOP:
                if self.debug_player:
                    logger.info("Calling _handle_video_stop handler")
                self._handle_video_stop(message)
            elif message.type == MessageType.VIDEO_SEEK:
                if self.debug_player:
                    logger.info("Calling _handle_video_seek handler")
                self._handle_video_seek(message)
            elif message.type == MessageType.VIDEO_VOLUME:
                if self.debug_player:
                    logger.info("Calling _handle_video_volume handler")
                self._handle_video_volume(message)
            elif message.type == MessageType.VIDEO_FULLSCREEN:
                if self.debug_player:
                    logger.info("Calling _handle_video_fullscreen handler")
                self._handle_video_fullscreen(message)
            elif message.type == MessageType.TEMPLATE_CHANGE:
                if self.debug_player:
                    logger.info("Calling _handle_template_change handler")
                self._handle_template_change(message)
            elif message.type == MessageType.OVERLAY_UPDATE:
                if self.debug_player:
                    logger.info("Calling _handle_overlay_update handler")
                self._handle_overlay_update(message)
            elif message.type == MessageType.STATUS_REQUEST:
                if self.debug_player:
                    logger.info("Calling _handle_status_request handler")
                self._handle_status_request(message)
            elif message.type == MessageType.START_INTRO:
                if self.debug_player:
                    logger.info("Calling _handle_start_intro handler")
                self._handle_start_intro(message)
            elif message.type == MessageType.PLAY_VIDEO_MATCH:
                if self.debug_player:
                    logger.info("Calling _handle_play_video_match handler")
                self._handle_play_video_match(message)
            else:
                if self.debug_player:
                    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 with loop control - 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
            
            # Extract loop control data
            loop_data = {}
            loop_fields = ['action', 'loop_mode', 'infinite_loop', 'loop_count', 'continuous_playback', 'repeat']
            for field in loop_fields:
                if field in message.data:
                    loop_data[field] = message.data[field]
            
            logger.debug(f"LOOP DEBUG: VIDEO_PLAY message received from {message.sender}")
            logger.debug(f"LOOP DEBUG: File path: {file_path}")
            logger.debug(f"LOOP DEBUG: Template name: {template_name}")
            logger.debug(f"LOOP DEBUG: Overlay data: {template_data}")
            logger.debug(f"LOOP DEBUG: Loop control data extracted: {loop_data}")
            logger.debug(f"LOOP DEBUG: Raw message data: {message.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 and loop data
            self.window.play_video(file_path, template_data, template_name, loop_data)
            logger.info("play_video() called successfully on main thread with loop support")
            
        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.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Template change message received")
                logger.debug(f"GREEN SCREEN DEBUG: Template name: {template_name}")
                logger.debug(f"GREEN SCREEN DEBUG: Reload template: {reload_template}")
                logger.debug(f"GREEN SCREEN DEBUG: Load specific template: {load_specific_template}")
            
            if self.window and hasattr(self.window, 'window_overlay'):
                overlay_view = self.window.window_overlay
                
                # Check video player state before template change
                if hasattr(self.window, 'media_player') and self.debug_overlay:
                    video_state = self.window.media_player.playbackState()
                    logger.debug(f"GREEN SCREEN DEBUG: Video playback state during template change: {video_state}")
                    logger.debug(f"GREEN SCREEN DEBUG: Video position: {self.window.media_player.position()}")
                    logger.debug(f"GREEN SCREEN DEBUG: Video duration: {self.window.media_player.duration()}")

                # Check overlay window transparency state
                if hasattr(self.window, 'overlay_window') and self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Overlay window geometry: {self.window.overlay_window.geometry()}")
                    logger.debug(f"GREEN SCREEN DEBUG: Overlay window visible: {self.window.overlay_window.isVisible()}")
                
                # CRITICAL FIX: Protect video context during template changes
                video_widget = None
                if hasattr(self.window, 'video_widget') and hasattr(self.window.video_widget, 'get_video_widget'):
                    video_widget = self.window.video_widget.get_video_widget()
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN FIX: Video widget state before template change - visible: {video_widget.isVisible() if video_widget else 'N/A'}")

                # Load specific template if requested and using WebEngine overlay
                if load_specific_template and isinstance(overlay_view, OverlayWebView):
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: About to load specific template: {load_specific_template}")
                        logger.debug(f"GREEN SCREEN FIX: Protecting video rendering during template load")
                    overlay_view.load_template(load_specific_template)
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: Specific template load initiated")
                # Otherwise reload current template if requested and using WebEngine overlay
                elif reload_template and isinstance(overlay_view, OverlayWebView):
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: About to reload current template")
                        logger.debug(f"GREEN SCREEN FIX: Protecting video rendering during template reload")
                    overlay_view.reload_current_template()
                    if self.debug_overlay:
                        logger.debug(f"GREEN SCREEN DEBUG: Current template reload initiated")
                
                # CRITICAL FIX: Force video widget refresh after template change
                if video_widget and self.debug_overlay:
                    logger.debug(f"GREEN SCREEN FIX: Forcing video widget refresh after template change")
                    video_widget.repaint()
                    video_widget.update()

                    # Force media player to refresh video output
                    if hasattr(self.window, 'media_player'):
                        logger.debug(f"GREEN SCREEN FIX: Refreshing media player video output")
                        current_output = self.window.media_player.videoOutput()
                        if current_output:
                            # Force video output refresh by briefly setting to None and back
                            self.window.media_player.setVideoOutput(None)
                            self.window.media_player.setVideoOutput(current_output)
                            logger.debug(f"GREEN SCREEN FIX: Media player video output refreshed")

                    logger.debug(f"GREEN SCREEN FIX: Video widget state after template change - visible: {video_widget.isVisible()}")

                # 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:
                        # Log data summary instead of full content to avoid cluttering logs with HTML
                        if self.debug_overlay:
                            data_keys = list(data_to_send.keys())
                            logger.debug(f"GREEN SCREEN DEBUG: Sending overlay data with keys: {data_keys}")
                        # 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")

                if self.debug_overlay:
                    logger.debug(f"GREEN SCREEN DEBUG: Template change handler completed")

        except Exception as e:
            if self.debug_overlay:
                logger.debug(f"GREEN SCREEN DEBUG: Template change handler failed: {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 _handle_start_intro(self, message: Message):
        """Handle START_INTRO message - find and play intro video with configured template sequence"""
        try:
            logger.info("Handling START_INTRO message")

            # Get match_id and fixture_id from message
            match_id = message.data.get("match_id")
            fixture_id = message.data.get("fixture_id")

            # Find the correct intro video file
            intro_video_path = self._find_intro_video_file(match_id)

            if intro_video_path:
                logger.info(f"Found intro video: {intro_video_path}")

                # Load intro templates configuration
                intro_templates = self._load_intro_templates_config()

                if intro_templates and intro_templates.get('templates'):
                    # Use configured template sequence
                    template_sequence = intro_templates['templates']
                    logger.info(f"Using configured template sequence: {[t['name'] for t in template_sequence]}")

                    # Set up loop control for intro video with template rotation
                    loop_data = {
                        'infinite_loop': True,  # Loop indefinitely until PLAY_VIDEO_MATCH
                        'continuous_playback': True,
                        'template_sequence': template_sequence,
                        'rotating_time': intro_templates.get('rotating_time', '05:00')
                    }

                    # Start with first template in sequence
                    first_template = template_sequence[0]['name'] if template_sequence else 'news_template'

                    # Play the intro video with template rotation
                    if self.window:
                        self.window.play_video(
                            str(intro_video_path),
                            template_data={"title": "Intro Video", "subtitle": "Preparing for match..."},
                            template_name=first_template,
                            loop_data=loop_data
                        )
                        logger.info(f"Intro video started with template rotation: {first_template}")
                    else:
                        logger.error("No window available for intro video playback")
                else:
                    # Fallback to default behavior if no templates configured
                    logger.warning("No intro templates configured, using default news_template")
                    loop_data = {
                        'infinite_loop': True,
                        'continuous_playback': True
                    }

                    if self.window:
                        self.window.play_video(
                            str(intro_video_path),
                            template_data={"title": "Intro Video", "subtitle": "Preparing for match..."},
                            template_name="news_template",
                            loop_data=loop_data
                        )
                        logger.info("Intro video started in loop mode with default template")
                    else:
                        logger.error("No window available for intro video playback")
            else:
                logger.warning("No intro video found, skipping intro playback")

        except Exception as e:
            logger.error(f"Failed to handle START_INTRO message: {e}")

    def _handle_play_video_match(self, message: Message):
        """Handle PLAY_VIDEO_MATCH message - stop intro loop and play match video"""
        try:
            logger.info("Handling PLAY_VIDEO_MATCH message")

            match_id = message.data.get("match_id")
            video_filename = message.data.get("video_filename")
            fixture_id = message.data.get("fixture_id")

            if not match_id or not video_filename:
                logger.error("Missing match_id or video_filename in PLAY_VIDEO_MATCH message")
                return

            # Stop the current intro video loop and template rotation
            if self.window and hasattr(self.window, 'media_player'):
                logger.info("Stopping intro video loop and template rotation")
                self.window.stop_playback()

                # Explicitly stop template rotation timer
                if hasattr(self.window, 'template_rotation_timer') and self.window.template_rotation_timer and self.window.template_rotation_timer.isActive():
                    self.window.template_rotation_timer.stop()
                    logger.info("Template rotation timer stopped for match video")

            # Find the match video file from the ZIP
            match_video_path = self._find_match_video_file(match_id, video_filename)

            if match_video_path:
                logger.info(f"Found match video: {match_video_path}")

                # Set match video tracking flags
                self.is_playing_match_video = True
                self.current_match_id = match_id
                self.current_match_video_filename = video_filename
                self.current_fixture_id = fixture_id

                # Reset loop state for match videos (they don't loop)
                self.loop_enabled = False
                self.infinite_loop = False
                self.loop_count = 0
                self.current_loop_iteration = 0

                # Play the match video (no looping)
                if self.window:
                    self.window.play_video(
                        str(match_video_path),
                        template_data={"title": f"Match {match_id}", "subtitle": "Live Action"},
                        template_name="news_template",
                        loop_data=None  # No looping for match videos
                    )
                    logger.info(f"Match video started: {video_filename}")
                else:
                    logger.error("No window available for match video playback")
            else:
                logger.error(f"Match video not found: {video_filename} for match {match_id}")

        except Exception as e:
            logger.error(f"Failed to handle PLAY_VIDEO_MATCH message: {e}")

    def _load_intro_templates_config(self) -> Optional[Dict[str, Any]]:
        """Load intro templates configuration from database with retry mechanism"""
        try:
            from ..database.manager import DatabaseManager
            from ..database.models import GameConfigModel
            import json
            import time

            logger.info("QtPlayer: Starting to load intro templates configuration")

            # Retry mechanism for database access during startup
            max_retries = 3
            retry_delay = 0.5  # 500ms delay between retries

            for attempt in range(max_retries):
                try:
                    logger.debug(f"QtPlayer: Database access attempt {attempt + 1}")

                    # Get database manager from message bus or create one
                    db_manager = None
                    if hasattr(self, 'message_bus') and self.message_bus:
                        logger.debug("QtPlayer: Trying to get db_manager from message bus")
                        # Try to get db_manager from web_dashboard component
                        try:
                            web_dashboard_queue = self.message_bus._queues.get('web_dashboard')
                            if web_dashboard_queue and hasattr(web_dashboard_queue, 'component'):
                                component = web_dashboard_queue.component
                                if hasattr(component, 'db_manager'):
                                    db_manager = component.db_manager
                                    logger.info(f"QtPlayer: Got db_manager from web_dashboard component (attempt {attempt + 1})")
                        except Exception as e:
                            logger.debug(f"QtPlayer: Could not get db_manager from message bus (attempt {attempt + 1}): {e}")

                    if not db_manager:
                        logger.debug("QtPlayer: Creating database manager directly")
                        # Fallback: create database manager directly
                        from ..config.settings import get_user_data_dir
                        db_path = get_user_data_dir() / "mbetterclient.db"
                        logger.debug(f"QtPlayer: Database path: {db_path}")
                        db_manager = DatabaseManager(str(db_path))
                        # Initialize the database manager
                        if not db_manager.initialize():
                            logger.warning(f"QtPlayer: Failed to initialize database manager in Qt player (attempt {attempt + 1})")
                            if attempt < max_retries - 1:
                                time.sleep(retry_delay)
                                continue
                            return self._get_default_intro_config()

                    logger.debug("QtPlayer: Database manager ready, getting session")
                    session = db_manager.get_session()
                    try:
                        logger.debug("QtPlayer: Querying for intro_templates_config")
                        # Get intro templates configuration from database
                        intro_config = session.query(GameConfigModel).filter_by(
                            config_key='intro_templates_config'
                        ).first()

                        if intro_config:
                            logger.debug(f"QtPlayer: Found intro config, value length: {len(intro_config.config_value)}")
                            try:
                                config = json.loads(intro_config.config_value)
                                logger.info(f"QtPlayer: Successfully loaded intro templates config from database: {len(config.get('templates', []))} templates")
                                return config
                            except (json.JSONDecodeError, TypeError) as e:
                                logger.warning(f"QtPlayer: Invalid JSON in intro templates config: {e}")
                                return self._get_default_intro_config()
                        else:
                            logger.info("QtPlayer: No intro templates config found in database, using defaults")
                            return self._get_default_intro_config()

                    finally:
                        session.close()

                except Exception as e:
                    logger.warning(f"QtPlayer: Database access attempt {attempt + 1} failed: {str(e)}")
                    if attempt < max_retries - 1:
                        time.sleep(retry_delay)
                        continue
                    else:
                        logger.error(f"QtPlayer: All database access attempts failed: {str(e)}")
                        return self._get_default_intro_config()

            # If we get here, all retries failed
            logger.error("QtPlayer: All retries exhausted, returning defaults")
            return self._get_default_intro_config()

        except Exception as e:
            logger.error(f"QtPlayer: Error loading intro templates from database: {str(e)}")
            return self._get_default_intro_config()

    def _get_default_intro_config(self) -> Dict[str, Any]:
        """Get default intro templates configuration"""
        return {
            'templates': [],
            'default_show_time': '00:30',
            'rotating_time': '05:00'
        }

    def _find_intro_video_file(self, match_id: int) -> Optional[Path]:
        """Find the correct intro video file based on priority"""
        try:
            # Priority 1: Check for INTRO.mp4 in the unzipped ZIP file of the match
            if match_id:
                from ..database.manager import DatabaseManager
                from ..config.settings import get_user_data_dir

                # Get database manager (assuming it's available via message bus or settings)
                # For now, we'll use a simplified approach
                user_data_dir = get_user_data_dir()
                temp_dir_pattern = f"match_{match_id}_"

                # Look for temp directories created by _unzip_match_zip_file
                import tempfile
                import os
                temp_base = Path(tempfile.gettempdir())

                for temp_dir in temp_base.glob(f"{temp_dir_pattern}*"):
                    if temp_dir.is_dir():
                        intro_file = temp_dir / "INTRO.mp4"
                        if intro_file.exists():
                            logger.info(f"Found INTRO.mp4 in match ZIP: {intro_file}")
                            return intro_file

            # Priority 2: Check for uploaded intro video
            # This would need to be implemented based on how uploaded intro videos are stored
            # For now, we'll skip this and go to priority 3

            # Priority 3: Fallback to INTRO.mp4 in assets directory
            assets_dir = Path(__file__).parent.parent / "assets"
            assets_intro = assets_dir / "INTRO.mp4"
            if assets_intro.exists():
                logger.info(f"Using fallback INTRO.mp4 from assets: {assets_intro}")
                return assets_intro

            logger.warning("No intro video found in any location")
            return None

        except Exception as e:
            logger.error(f"Failed to find intro video file: {e}")
            return None

    def _find_match_video_file(self, match_id: int, video_filename: str) -> Optional[Path]:
        """Find the match video file from the unzipped ZIP"""
        try:
            import tempfile
            from pathlib import Path

            # Look for temp directories created by _unzip_match_zip_file
            temp_base = Path(tempfile.gettempdir())
            temp_dir_pattern = f"match_{match_id}_"

            for temp_dir in temp_base.glob(f"{temp_dir_pattern}*"):
                if temp_dir.is_dir():
                    video_file = temp_dir / video_filename
                    if video_file.exists():
                        logger.info(f"Found match video: {video_file}")
                        return video_file

            logger.warning(f"Match video not found: {video_filename} for match {match_id}")
            return None

        except Exception as e:
            logger.error(f"Failed to find match video file: {e}")
            return None

    def _start_template_rotation(self):
        """Start the template rotation timer"""
        try:
            if not self.template_sequence or len(self.template_sequence) <= 1:
                logger.debug("Template rotation not needed - insufficient templates")
                return

            # Parse rotating time (format: MM:SS)
            try:
                minutes, seconds = map(int, self.rotating_time.split(':'))
                rotation_interval_ms = (minutes * 60 + seconds) * 1000  # Convert to milliseconds
            except (ValueError, AttributeError):
                logger.warning(f"Invalid rotating_time format '{self.rotating_time}', using default 5 minutes")
                rotation_interval_ms = 5 * 60 * 1000  # Default 5 minutes

            logger.info(f"Starting template rotation every {rotation_interval_ms}ms ({self.rotating_time})")

            # Stop any existing timer
            if self.template_rotation_timer and self.template_rotation_timer.isActive():
                self.template_rotation_timer.stop()

            # Start the rotation timer
            self.template_rotation_timer.start(rotation_interval_ms)
            self.current_template_index = 0  # Reset to first template

        except Exception as e:
            logger.error(f"Failed to start template rotation: {e}")

    def _rotate_template(self):
        """Rotate to the next template in the sequence"""
        try:
            if not self.template_sequence or len(self.template_sequence) == 0:
                logger.debug("No template sequence available for rotation")
                return

            # Move to next template
            self.current_template_index = (self.current_template_index + 1) % len(self.template_sequence)
            next_template = self.template_sequence[self.current_template_index]['name']

            logger.info(f"Rotating to template: {next_template} (index {self.current_template_index})")

            # Load the new template
            if hasattr(self, 'window_overlay') and isinstance(self.window_overlay, OverlayWebView):
                self.window_overlay.load_template(next_template)
                logger.info(f"Template rotated to: {next_template}")
            else:
                logger.warning("No WebEngine overlay available for template rotation")

        except Exception as e:
            logger.error(f"Failed to rotate template: {e}")

    def _send_match_video_done_message(self):
        """Send PLAY_VIDEO_MATCH_DONE message when match video finishes"""
        try:
            if (self.current_match_id is not None and
                self.current_match_video_filename is not None):

                from ..core.message_bus import MessageBuilder

                done_message = MessageBuilder.play_video_match_done(
                    sender=self.name,
                    match_id=self.current_match_id,
                    video_filename=self.current_match_video_filename,
                    fixture_id=self.current_fixture_id
                )

                self.message_bus.publish(done_message, broadcast=True)
                logger.info(f"Sent PLAY_VIDEO_MATCH_DONE for match {self.current_match_id}, video {self.current_match_video_filename}")

        except Exception as e:
            logger.error(f"Failed to send match video done message: {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}")
