"""
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):
        super().__init__(parent)
        self.web_channel = None
        self.overlay_channel = None
        self.current_template = "default.html"
        
        # Built-in templates directory (bundled with app)
        self.builtin_templates_dir = Path(__file__).parent / "templates"
        
        # Persistent uploaded templates directory (user data)
        self.uploaded_templates_dir = self._get_persistent_templates_dir()
        self.uploaded_templates_dir.mkdir(parents=True, exist_ok=True)
        
        # Primary templates directory for backwards compatibility
        self.templates_dir = self.builtin_templates_dir
        
        self.setup_web_view()
        self._setup_custom_scheme()
        logger.info(f"OverlayWebView initialized - builtin: {self.builtin_templates_dir}, uploaded: {self.uploaded_templates_dir}")
    
    def setup_web_view(self):
        """Setup web view with proper transparency for overlay"""
        logger.debug("OverlayWebView.setup_web_view() - Starting setup")

        # 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:
            # 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:
            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():
                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():
                    logger.error(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))
                
                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:
            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.error(f"GREEN SCREEN FIX: Overlay lost visibility after template load, forcing recovery")
                self.show()
                self.raise_()
                logger.error(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.error(f"GREEN SCREEN FIX: Parent overlay window lost visibility, forcing recovery")
                parent.show()
                parent.raise_()
                logger.error(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.error(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.error(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.error(f"GREEN SCREEN FIX: Video widget repainted during template load")
        except Exception as e:
            logger.error(f"GREEN SCREEN FIX: Failed to protect video context: {e}")
    
    def reload_current_template(self):
        """Reload the current template"""
        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)
        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):
        super().__init__()
        self.settings = settings
        self.mutex = QMutex()
        self.thread_pool = QThreadPool()
        self.thread_pool.setMaxThreadCount(4)
        self._message_bus = message_bus  # Store message bus reference for shutdown messages
        
        self.setup_ui()
        self.setup_media_player()
        self.setup_timers()
        
        logger.info("PlayerWindow initialized")
    
    def _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:
            self.window_overlay = OverlayWebView(self.overlay_window)
            logger.debug("PlayerWindow: Created QWebEngineView overlay as separate window")

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

        # Window settings - FORCE window to display properly
        if self.settings.fullscreen:
            self.showFullScreen()
        else:
            self.resize(self.settings.window_width, self.settings.window_height)
            
        # CRITICAL: Force window display and painting
        self.show()
        self.raise_()  # Bring to front
        self.activateWindow()  # Give focus
        self.repaint()  # Force repaint
        
        # Force central widget to repaint too
        if self.centralWidget():
            self.centralWidget().repaint()
        
        if self.settings.always_on_top:
            self.setWindowFlags(self.windowFlags() | Qt.WindowType.WindowStaysOnTopHint)
            self.show()  # Reshow after flag change
        
        # Enable overlay window positioning - threading issues resolved
        logger.info("ENABLING overlay window with proper main thread positioning")
        QTimer.singleShot(100, self._sync_overlay_position)
        
        # Setup menu - REMOVED for clean interface
        # self.setup_menu()
    
    def _sync_overlay_position(self):
        """Synchronize overlay window position with main player window"""
        try:
            if hasattr(self, 'overlay_window') and self.overlay_window:
                # 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")
                
                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:
            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
        
        # Set volume
        self.audio_output.setVolume(self.settings.volume)
        self.audio_output.setMuted(self.settings.mute)
    
    def setup_timers(self):
        """Setup various timers"""
        # Overlay update timer
        self.overlay_timer = QTimer()
        self.overlay_timer.timeout.connect(self.update_overlay_periodically)
        self.overlay_timer.start(1000)  # Update every second
        
        # Mouse tracking for window interactions (no controls to show/hide)
        self.setMouseTracking(True)
    
    def open_file_dialog(self):
        """Open file dialog to select video"""
        from PyQt6.QtWidgets import QFileDialog
        
        file_path, _ = QFileDialog.getOpenFileName(
            self,
            "Open Video File",
            "",
            "Video Files (*.mp4 *.avi *.mkv *.mov *.wmv *.flv *.webm *.m4v);;All Files (*)"
        )
        
        if file_path:
            self.play_video(file_path)
    
    def play_video(self, file_path: str, template_data: Dict[str, Any] = None, template_name: str = None, 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)
                
                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
                self.loop_enabled = False
                self.infinite_loop = False
                self.loop_count = 0
                self.current_loop_iteration = 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.error(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.error(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.error(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.error(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.error(f"GREEN SCREEN FIX: Forcing video widget refresh after template load")
                    video_widget.repaint()
                    video_widget.update()
                    logger.error(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))
                
                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()
    
    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.error(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
            logger.error(f"LOOP DEBUG: END OF MEDIA DETECTED!")
            logger.error(f"LOOP DEBUG: Loop enabled: {self.loop_enabled}")
            logger.error(f"LOOP DEBUG: Infinite loop: {self.infinite_loop}")
            logger.error(f"LOOP DEBUG: Current iteration: {self.current_loop_iteration}")
            logger.error(f"LOOP DEBUG: Loop count: {self.loop_count}")
            
            if self.loop_enabled:
                logger.error(f"LOOP DEBUG: Processing loop restart logic...")
                if self.infinite_loop:
                    # Infinite loop - restart immediately
                    self.current_loop_iteration += 1
                    logger.error(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.error(f"LOOP DEBUG: FINITE LOOP - Starting iteration {self.current_loop_iteration}/{self.loop_count}")
                    self._restart_video()
                else:
                    logger.error(f"LOOP DEBUG: Loop completed after {self.current_loop_iteration} iterations - stopping playback")
                    self.loop_enabled = False
            else:
                logger.error(f"LOOP DEBUG: No loop enabled - playback finished")
    
    def _restart_video(self):
        """Restart video playback for looping"""
        try:
            logger.error(f"LOOP DEBUG: _restart_video called")
            logger.error(f"LOOP DEBUG: Current file path: {self.current_file_path}")
            logger.error(f"LOOP DEBUG: Media player state: {self.media_player.playbackState()}")
            
            if self.current_file_path:
                logger.error(f"LOOP DEBUG: Restarting video for loop: {self.current_file_path}")
                url = QUrl.fromLocalFile(self.current_file_path)
                logger.error(f"LOOP DEBUG: Created QUrl: {url.toString()}")
                
                self.media_player.setSource(url)
                logger.error(f"LOOP DEBUG: Set media player source")
                
                self.media_player.play()
                logger.error(f"LOOP DEBUG: Called media player play()")
                logger.error(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.error(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.error(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
            
            # 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'):
                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
            
            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
                logger.debug(f"GREEN SCREEN DEBUG: Updating native overlay")
                overlay_view.update_overlay_data(cleaned_data)
                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
                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():
                    logger.error(f"GREEN SCREEN FIX: WebEngine overlay not visible, forcing visibility recovery")
                    overlay_view.show()
                    overlay_view.raise_()
                    logger.error(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():
                        logger.error(f"GREEN SCREEN FIX: Parent overlay window not visible, forcing visibility")
                        self.overlay_window.show()
                        self.overlay_window.raise_()
                        logger.error(f"GREEN SCREEN FIX: Parent overlay window visibility forced")
                
                if self._is_webengine_ready(overlay_view):
                    logger.debug(f"GREEN SCREEN DEBUG: WebEngine ready, updating overlay")
                    overlay_view.update_overlay_data(cleaned_data)
                    logger.debug("WebEngine overlay updated successfully")
                    logger.debug(f"GREEN SCREEN DEBUG: WebEngine overlay update completed")
                    return True
                else:
                    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)}")
                logger.debug(f"GREEN SCREEN DEBUG: Unknown overlay type: {type(overlay_view)}")
                return False
        except Exception as e:
            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.error(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.error(f"GREEN SCREEN FIX: Video widget repainted after template load")
            
            # Force media player to refresh video output
            if hasattr(self, 'media_player'):
                logger.error(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.error(f"GREEN SCREEN FIX: Media player video output refreshed after template load")
        except Exception as e:
            logger.error(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):
        super().__init__()
        self.name = "qt_player"
        self.message_bus = message_bus
        self.settings = settings
        self.app: Optional[QApplication] = None
        self.window: Optional[PlayerWindow] = None
        self.mutex = QMutex()
        
        # Register message queue
        logger.info(f"Registering QtVideoPlayer component with message bus - name: '{self.name}'")
        self.message_queue = self.message_bus.register_component(self.name)
        logger.info(f"QtVideoPlayer component registered successfully - queue: {self.message_queue}")
        
        # Message processing timer (runs on Qt main thread)
        self.message_timer: Optional[QTimer] = None
        
        logger.info("QtVideoPlayer (PyQt6) initialized")
    
    def _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
                self.window = PlayerWindow(self.settings, self.message_bus)
                
                # CRITICAL: Connect signal to slot for cross-thread video playback
                self.play_video_signal.connect(self.window.play_video, Qt.ConnectionType.QueuedConnection)
                logger.info("Connected play_video_signal to PlayerWindow.play_video with QueuedConnection")
                
                # 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)
                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
                    logger.info(f"QtPlayer RECEIVED message: {message.type.value} from {message.sender}")
                    logger.info(f"Message data: {message.data}")
                    
                    # Process message directly on main thread - no threading issues!
                    self._process_message(message)
                    
                except Exception as e:
                    logger.error(f"QtPlayer message processing error: {e}")
                    break
            
            # Send periodic progress updates if playing
            if (self.window and
                self.window.media_player.playbackState() == QMediaPlayer.PlaybackState.PlayingState):
                self._send_progress_update()
                
        except Exception as e:
            logger.error(f"QtPlayer main thread message processing failed: {e}")
    
    def run(self):
        """Run the Qt event loop (this should be called on the main thread)"""
        try:
            logger.info("QtVideoPlayer starting Qt event loop")
            
            # Send ready status
            logger.info(f"QtVideoPlayer sending ready status from component: {self.name}")
            ready_message = MessageBuilder.system_status(
                sender=self.name,
                status="ready",
                details={"fullscreen": self.settings.fullscreen, "version": "PyQt6-2.0.0"}
            )
            publish_result = self.message_bus.publish(ready_message)
            logger.info(f"Ready status message publish result: {publish_result}")
            
            # Start message processing timer on main thread
            self.start_message_processing()
            
            # Run Qt event loop (this blocks until app quits)
            if self.app:
                return self.app.exec()
            else:
                logger.error("No QApplication instance available")
                return 1
                
        except Exception as e:
            logger.error(f"QtVideoPlayer run failed: {e}")
            return 1
    
    def _setup_signal_handlers(self):
        """Setup Qt-specific signal handlers - let main app handle Python signals"""
        try:
            # Only handle Qt application aboutToQuit signal
            # Let the main application handle SIGINT/SIGTERM to avoid conflicts
            if self.app:
                self.app.aboutToQuit.connect(self._handle_app_quit)
            
            logger.debug("Qt signal handlers setup completed")
            
        except Exception as e:
            logger.warning(f"Qt signal handler setup failed: {e}")
    
    def _handle_app_quit(self):
        """Handle Qt application quit signal"""
        logger.info("Qt application about to quit")
        
        # Ensure message processing timer is stopped
        self.stop_message_processing()
    
    def shutdown(self):
        """Shutdown video player"""
        try:
            logger.info("Shutting down QtVideoPlayer...")
            
            # Stop message processing timer
            self.stop_message_processing()
            
            with QMutexLocker(self.mutex):
                if self.window:
                    self.window.close()
                    self.window = None
                
                if self.app and not self.app.closingDown():
                    self.app.quit()
            
        except Exception as e:
            logger.error(f"QtVideoPlayer shutdown error: {e}")
    
    def _process_message(self, message: Message):
        """Process received message by routing to appropriate handlers"""
        try:
            logger.info(f"QtPlayer processing message type: {message.type.value}")
            
            # Route messages to appropriate handlers
            if message.type == MessageType.VIDEO_PLAY:
                logger.info("Calling _handle_video_play handler")
                self._handle_video_play(message)
            elif message.type == MessageType.VIDEO_PAUSE:
                logger.info("Calling _handle_video_pause handler")
                self._handle_video_pause(message)
            elif message.type == MessageType.VIDEO_STOP:
                logger.info("Calling _handle_video_stop handler")
                self._handle_video_stop(message)
            elif message.type == MessageType.VIDEO_SEEK:
                logger.info("Calling _handle_video_seek handler")
                self._handle_video_seek(message)
            elif message.type == MessageType.VIDEO_VOLUME:
                logger.info("Calling _handle_video_volume handler")
                self._handle_video_volume(message)
            elif message.type == MessageType.VIDEO_FULLSCREEN:
                logger.info("Calling _handle_video_fullscreen handler")
                self._handle_video_fullscreen(message)
            elif message.type == MessageType.TEMPLATE_CHANGE:
                logger.info("Calling _handle_template_change handler")
                self._handle_template_change(message)
            elif message.type == MessageType.OVERLAY_UPDATE:
                logger.info("Calling _handle_overlay_update handler")
                self._handle_overlay_update(message)
            elif message.type == MessageType.STATUS_REQUEST:
                logger.info("Calling _handle_status_request handler")
                self._handle_status_request(message)
            else:
                logger.warning(f"No handler for message type: {message.type.value}")
                
        except Exception as e:
            logger.error(f"Failed to process message: {e}")
            import traceback
            logger.error(f"Full traceback: {traceback.format_exc()}")
    
    def _on_position_changed(self, position: int, duration: int):
        """Handle position changes from player window"""
        try:
            # Send progress update via message bus
            if duration > 0:
                percentage = (position / duration) * 100
                progress_message = MessageBuilder.video_progress(
                    sender=self.name,
                    position=position / 1000.0,  # Convert to seconds
                    duration=duration / 1000.0,  # Convert to seconds
                    percentage=percentage
                )
                self.message_bus.publish(progress_message, broadcast=True)
        except Exception as e:
            logger.error(f"Failed to send progress update: {e}")
    
    def _on_video_loaded(self, file_path: str):
        """Handle video loaded event"""
        try:
            logger.info(f"Video loaded: {file_path}")
            # Send video loaded status message
            loaded_message = MessageBuilder.system_status(
                sender=self.name,
                status="video_loaded",
                details={"file_path": file_path}
            )
            self.message_bus.publish(loaded_message, broadcast=True)
        except Exception as e:
            logger.error(f"Failed to handle video loaded event: {e}")
    
    def _send_progress_update(self):
        """Send video progress update"""
        try:
            if self.window and self.window.media_player.duration() > 0:
                position = self.window.media_player.position()
                duration = self.window.media_player.duration()
                percentage = (position / duration) * 100 if duration > 0 else 0
                
                progress_message = MessageBuilder.video_progress(
                    sender=self.name,
                    position=position / 1000.0,  # Convert to seconds
                    duration=duration / 1000.0,  # Convert to seconds
                    percentage=percentage
                )
                self.message_bus.publish(progress_message, broadcast=True)
                
        except Exception as e:
            logger.error(f"Failed to send progress update: {e}")
    
    # Message handlers for various video control commands
    def _handle_video_play(self, message: Message):
        """Handle video play message 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.error(f"LOOP DEBUG: VIDEO_PLAY message received from {message.sender}")
            logger.error(f"LOOP DEBUG: File path: {file_path}")
            logger.error(f"LOOP DEBUG: Template name: {template_name}")
            logger.error(f"LOOP DEBUG: Overlay data: {template_data}")
            logger.error(f"LOOP DEBUG: Loop control data extracted: {loop_data}")
            logger.error(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", "")
            
            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'):
                    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'):
                    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()
                    logger.error(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):
                    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)
                    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):
                    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()
                    logger.debug(f"GREEN SCREEN DEBUG: Current template reload initiated")
                
                # CRITICAL FIX: Force video widget refresh after template change
                if video_widget:
                    logger.error(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.error(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.error(f"GREEN SCREEN FIX: Media player video output refreshed")
                    
                    logger.error(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
                        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")
                
                logger.debug(f"GREEN SCREEN DEBUG: Template change handler completed")
                
        except Exception as e:
            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 _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}")
