"""
RTSP Streamer component for headless video playback and overlay streaming

SEPARATED PROCESS ARCHITECTURE:
This implementation uses two separate processes:
1. Video Feeder Process (video_feeder.py) - Reads video files, outputs raw frames
2. FFmpeg Process - Encodes raw frames to HLS

The main application controls the video feeder via JSON commands through stdin.

Architecture:
  Main App → JSON commands → Video Feeder Process → raw RGB frames → FFmpeg → HLS

Benefits:
- Process isolation: Video processing crashes don't affect main app
- Independent restart: Video feeder can be restarted without restarting app
- Better resource management: Separate CPU/memory accounting
- Cleaner architecture: Single responsibility principle
"""

import sys
import time
import logging
import threading
import subprocess
import json
import http.server
import socketserver
import os
import shutil
import tempfile
from pathlib import Path
from typing import Optional, Dict, Any, List
from urllib.parse import urlparse

from .thread_manager import ThreadedComponent
from .message_bus import MessageBus, Message, MessageType, MessageBuilder

logger = logging.getLogger(__name__)


class VideoFeederProcess:
    """Manages the separate video feeder process
    
    Communication:
    - stdin: JSON commands to control playback
    - stdout: Raw RGB frames (piped to FFmpeg)
    - stderr: JSON status messages
    """
    
    def __init__(self, video_feeder_script: str):
        self.video_feeder_script = video_feeder_script
        self.process = None
        self.status_thread = None
        self.running = False
        
        # Status callbacks
        self.on_status = None  # Callback for status messages
        
        logger.info("VideoFeederProcess initialized")
    
    def start(self, ffmpeg_stdin) -> bool:
        """Start the video feeder process
        
        Args:
            ffmpeg_stdin: The stdin pipe of the FFmpeg process to pipe frames to
        
        Returns:
            True if started successfully
        """
        try:
            if self.process is not None:
                logger.warning("Video feeder process already running")
                return False
            
            logger.info(f"Starting video feeder process: {self.video_feeder_script}")
            
            # Start the video feeder process
            # stdout goes to FFmpeg stdin, we read stderr for status
            self.process = subprocess.Popen(
                [sys.executable, self.video_feeder_script],
                stdin=subprocess.PIPE,   # We send commands here
                stdout=ffmpeg_stdin,      # Frames go directly to FFmpeg
                stderr=subprocess.PIPE,   # Status messages come here
                text=False,               # Binary mode for stdout
                bufsize=0                 # Unbuffered
            )
            
            self.running = True
            
            # Start thread to read status messages from stderr
            self.status_thread = threading.Thread(target=self._read_status, daemon=True)
            self.status_thread.start()
            
            logger.info(f"Video feeder process started, PID: {self.process.pid}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to start video feeder process: {e}")
            return False
    
    def _read_status(self):
        """Thread to read status messages from video feeder stderr"""
        while self.running and self.process:
            try:
                line = self.process.stderr.readline()
                if not line:
                    if self.process.poll() is not None:
                        logger.info("Video feeder process stderr closed")
                        break
                    continue
                
                # Decode and parse JSON status
                try:
                    line = line.decode('utf-8').strip()
                    if not line:
                        continue
                    
                    status = json.loads(line)
                    logger.debug(f"Video feeder status: {status}")
                    
                    # Call status callback if set
                    if self.on_status:
                        self.on_status(status)
                        
                except json.JSONDecodeError as e:
                    logger.warning(f"Invalid JSON from video feeder: {line}")
                    
            except Exception as e:
                logger.error(f"Error reading video feeder status: {e}")
                time.sleep(0.1)
        
        logger.info("Video feeder status thread ended")
    
    def send_command(self, command: Dict[str, Any]) -> bool:
        """Send a JSON command to the video feeder
        
        Args:
            command: Command dictionary to send
        
        Returns:
            True if sent successfully
        """
        try:
            if not self.process or self.process.poll() is not None:
                logger.warning("Video feeder process not running")
                return False
            
            # Send JSON command followed by newline
            cmd_str = json.dumps(command) + "\n"
            self.process.stdin.write(cmd_str.encode('utf-8'))
            self.process.stdin.flush()
            
            logger.debug(f"Sent command to video feeder: {command}")
            return True
            
        except Exception as e:
            logger.error(f"Failed to send command to video feeder: {e}")
            return False
    
    def play(self, video_path: str, loop: bool = False) -> bool:
        """Send play command"""
        return self.send_command({
            "command": "play",
            "video": video_path,
            "loop": loop
        })
    
    def pause(self) -> bool:
        """Send pause command"""
        return self.send_command({"command": "pause"})
    
    def resume(self) -> bool:
        """Send resume command"""
        return self.send_command({"command": "resume"})
    
    def stop(self) -> bool:
        """Send stop command"""
        return self.send_command({"command": "stop"})
    
    def quit(self) -> bool:
        """Send quit command"""
        return self.send_command({"command": "quit"})
    
    def is_running(self) -> bool:
        """Check if the process is running"""
        return self.process is not None and self.process.poll() is None
    
    def stop_process(self):
        """Stop the video feeder process"""
        try:
            self.running = False
            
            if self.process:
                logger.info("Stopping video feeder process...")
                
                # Try graceful quit first
                try:
                    self.quit()
                    self.process.wait(timeout=2)
                except subprocess.TimeoutExpired:
                    # Force terminate
                    self.process.terminate()
                    try:
                        self.process.wait(timeout=2)
                    except subprocess.TimeoutExpired:
                        self.process.kill()
                        self.process.wait()
                
                self.process = None
                logger.info("Video feeder process stopped")
            
            if self.status_thread:
                self.status_thread.join(timeout=2)
                self.status_thread = None
                
        except Exception as e:
            logger.error(f"Error stopping video feeder process: {e}")


class RTSPStreamer(ThreadedComponent):
    """HLS streamer that implements qt-player functionality in headless mode
    
    SEPARATED PROCESS ARCHITECTURE:
    - Video Feeder Process: Reads video files, outputs raw frames
    - FFmpeg Process: Encodes raw frames to HLS
    - Main App: Controls via JSON commands
    
    Benefits:
    - Process isolation
    - Independent restart capability
    - Better resource management
    """
    
    def __init__(self, message_bus: MessageBus, settings, debug_player: bool = False, test_mode: bool = False, config_manager=None):
        super().__init__(name="headless_player", message_bus=message_bus)
        self.settings = settings
        self.debug_player = debug_player
        self.test_mode = test_mode
        self.config_manager = config_manager

        # RTSP streaming settings (kept for compatibility)
        self.rtsp_port = 8554
        self.stream_name = "mbetterc_stream"
        self.rtsp_url = f"rtsp://127.0.0.1:{self.rtsp_port}/{self.stream_name}"

        # HTTP streaming settings (configurable)
        self.http_port = getattr(settings, 'streamer_port', 5884)
        self.http_url = f"http://127.0.0.1:{self.http_port}/{self.stream_name}.m3u8"
        
        # Video bitrate settings (configurable via config_manager)
        # Default: High=10000k, Medium=6667k (2/3), Low=3333k (1/3)
        self._load_bitrate_settings()

        # Video processing - SEPARATE PROCESSES
        self.ffmpeg_process = None
        self.video_feeder = None
        self.is_streaming = False

        # Path to video feeder script
        self.video_feeder_script = os.path.join(
            os.path.dirname(__file__), 
            "video_feeder.py"
        )

        # Overlay data (simulated for headless mode)
        self.overlay_data = {
            'title': 'MbetterClient Headless Stream',
            'subtitle': 'RTSP Streaming Active',
            'ticker': 'Headless mode - no GUI display'
        }

        # Match tracking (similar to Qt player)
        self.is_playing_match_video = False
        self.current_match_id = None
        self.current_match_video_filename = None
        self.current_fixture_id = None
        self.current_result = None

        # Video Phase State Machine for HLS synchronization
        # Phases: 'idle', 'intro', 'match', 'result'
        self.video_phase = 'idle'
        self.phase_start_time = 0.0
        self.phase_video_duration = 0.0
        self.hls_delay_compensation = 5.0  # Default 5 seconds delay compensation
        self.result_data = None  # Store result data for overlay
        self.pending_phase_change = None  # Pending phase change after delay

        # Video playback state management
        self.current_video_type = 'black_screen'
        self.video_loop_enabled = True
        self.current_video_looping = False
        self.pending_video_request = None
        self.video_end_timer = None
        self.current_video_duration = 0.0
        self.video_start_time = 0.0
        self.current_video_path = None
        self.intro_video_path = None
        self._switching_video = False

        # Health monitoring
        self.health_check_interval = 10.0  # Check every 10 seconds
        self.stalled_threshold = 30.0  # Consider stalled after 30 seconds of no activity
        self.last_frame_activity = time.time()  # Track last frame activity
        self.video_feeder_healthy = True  # Track video feeder health
        self.consecutive_stalled_count = 0  # Count consecutive stalled detections
        self.max_stalled_count = 3  # Restart after 3 consecutive stalled detections
        self.last_health_check = time.time()
        self.auto_restart_enabled = True  # Enable automatic restart on stall

        # OVER/UNDER video sequence handling
        self.over_under_sequence = []
        self.current_over_under_index = 0
        self.is_playing_over_under = False

        # HTTP server for HLS streaming
        self.http_server = None
        self.hls_dir = None

        logger.info("RTSPStreamer initialized (separated process architecture)")

    def _load_bitrate_settings(self):
        """Load bitrate settings from config manager or use defaults
        
        Bitrate hierarchy:
        - High: configured value (default 10000 kbps)
        - Medium: 2/3 of High
        - Low: 1/3 of High
        """
        # Default high bitrate (10 Mbps)
        default_high_bitrate = 10000
        
        try:
            if self.config_manager:
                streaming_config = self.config_manager.get_section_config("streaming")
                if streaming_config:
                    high_bitrate = streaming_config.get("video_bitrate_high", default_high_bitrate)
                else:
                    high_bitrate = default_high_bitrate
            else:
                high_bitrate = default_high_bitrate
            
            # Ensure it's an integer
            high_bitrate = int(high_bitrate)
            
        except Exception as e:
            logger.warning(f"Failed to load bitrate settings: {e}, using defaults")
            high_bitrate = default_high_bitrate
        
        # Calculate medium (2/3) and low (1/3) bitrates
        medium_bitrate = int(high_bitrate * 2 / 3)
        low_bitrate = int(high_bitrate / 3)
        
        # Store as instance variables for use in FFmpeg command
        self.bitrate_high = high_bitrate
        self.bitrate_medium = medium_bitrate
        self.bitrate_low = low_bitrate
        
        # Calculate maxrate and bufsize (20% higher than bitrate)
        self.maxrate_high = int(high_bitrate * 1.2)
        self.maxrate_medium = int(medium_bitrate * 1.2)
        self.maxrate_low = int(low_bitrate * 1.2)
        
        self.bufsize_high = int(high_bitrate * 1.5)
        self.bufsize_medium = int(medium_bitrate * 1.5)
        self.bufsize_low = int(low_bitrate * 2)
        
        logger.info(f"Bitrate settings: High={high_bitrate}k, Medium={medium_bitrate}k, Low={low_bitrate}k")

    def initialize(self) -> bool:
        """Initialize the RTSP streamer"""
        try:
            logger.info("Initializing RTSP streamer (separated process)...")

            # Clean up any orphaned files from previous runs
            self._cleanup_orphaned_files()

            # Register with message bus
            self.message_bus.register_component(self.name)

            # Check if ffmpeg is available
            if not self._check_ffmpeg():
                logger.error("FFmpeg not found - required for RTSP streaming")
                return False

            # Check if video feeder script exists
            if not os.path.exists(self.video_feeder_script):
                logger.error(f"Video feeder script not found: {self.video_feeder_script}")
                return False

            # Subscribe to messages
            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.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.START_INTRO, self._handle_start_intro)
            self.message_bus.subscribe(self.name, MessageType.PLAY_VIDEO_MATCH, self._handle_play_video_match)
            self.message_bus.subscribe(self.name, MessageType.PLAY_VIDEO_RESULT, self._handle_play_video_result)
            self.message_bus.subscribe(self.name, MessageType.GAME_STATUS, self._handle_game_status)

            # Start continuous streaming
            logger.info("Starting continuous HLS streaming...")
            if not self._start_continuous_streaming():
                logger.error("Failed to start continuous streaming")
                return False

            logger.info(f"HLS streamer initialized - continuous streaming at {self.http_url}")
            return True

        except Exception as e:
            logger.error(f"RTSP streamer initialization failed: {e}")
            return False

    def _cleanup_orphaned_files(self):
        """Clean up orphaned files from previous runs"""
        try:
            import glob
            temp_dir = tempfile.gettempdir()
            
            # Kill any orphaned ffmpeg processes
            try:
                subprocess.run(['pkill', '-9', 'ffmpeg'], capture_output=True, timeout=5)
                logger.info("Killed any orphaned FFmpeg processes")
            except Exception as e:
                logger.debug(f"No FFmpeg processes to kill: {e}")
            
            # Kill any orphaned video_feeder processes
            try:
                subprocess.run(['pkill', '-f', 'video_feeder.py'], capture_output=True, timeout=5)
                logger.info("Killed any orphaned video feeder processes")
            except Exception as e:
                logger.debug(f"No video feeder processes to kill: {e}")
            
            # Remove orphaned HLS directories
            hls_pattern = os.path.join(temp_dir, "mbetterc_hls_*")
            for hls_dir in glob.glob(hls_pattern):
                try:
                    shutil.rmtree(hls_dir)
                    logger.debug(f"Removed orphaned HLS directory: {hls_dir}")
                except Exception as e:
                    logger.warning(f"Failed to remove HLS directory {hls_dir}: {e}")
            
            logger.info("Orphaned files cleanup completed")
            
        except Exception as e:
            logger.warning(f"Error during orphaned files cleanup: {e}")

    def run(self):
        """Main run loop for RTSP streamer"""
        logger.info("RTSP streamer main loop started")

        # If test mode is enabled, automatically start intro video after a short delay
        if self.test_mode:
            logger.info("Test mode enabled - will automatically start intro video")
            threading.Timer(3.0, self._start_test_intro).start()

        last_heartbeat = time.time()

        while self.running:
            try:
                # Process messages
                message = self.message_bus.get_message(self.name, timeout=1.0)
                if message:
                    self._process_message(message)

                # Check streaming status
                if self.is_streaming:
                    # Check if FFmpeg process is still running
                    if self.ffmpeg_process and self.ffmpeg_process.poll() is not None:
                        logger.warning("FFmpeg process terminated unexpectedly")
                        if not self._switching_video:
                            self._restart_streaming()

                    # Check if video feeder process is still running
                    if self.video_feeder and not self.video_feeder.is_running():
                        logger.warning("Video feeder process terminated unexpectedly")
                        if not self._switching_video:
                            self._restart_streaming()

                # Check video completion via timer
                if self.video_end_timer and not self.video_end_timer.is_alive():
                    logger.info(f"Video completion timer expired for {self.current_video_type}")
                    self._handle_video_completion()
                    self.video_end_timer = None

                # Health monitoring - check for stalled processes
                current_time = time.time()
                if current_time - self.last_health_check >= self.health_check_interval:
                    self._perform_health_check()
                    self.last_health_check = current_time

                # Send heartbeat every 10 seconds
                current_time = time.time()
                if current_time - last_heartbeat >= 10.0:
                    self.heartbeat()
                    last_heartbeat = current_time

                time.sleep(0.1)

            except Exception as e:
                logger.error(f"RTSP streamer error: {e}")
                time.sleep(1)

        logger.info("RTSP streamer main loop ended")

    def shutdown(self):
        """Shutdown the RTSP streamer"""
        logger.info("Shutting down RTSP streamer...")

        # Stop video feeder process
        if self.video_feeder:
            self.video_feeder.stop_process()

        # Stop FFmpeg
        self._stop_ffmpeg_process()

        # Stop HTTP server
        self._stop_http_server()

        super().shutdown()

    def _check_ffmpeg(self) -> bool:
        """Check if ffmpeg is available"""
        try:
            result = subprocess.run(['ffmpeg', '-version'],
                                  capture_output=True, text=True, timeout=5)
            if result.returncode == 0:
                logger.info("FFmpeg found and available")
                return True
            else:
                logger.error("FFmpeg not working properly")
                return False
        except (subprocess.TimeoutExpired, FileNotFoundError, subprocess.SubprocessError):
            logger.error("FFmpeg not found in PATH")
            return False

    def _start_continuous_streaming(self) -> bool:
        """Start continuous HLS streaming with separate processes
        
        ADAPTIVE BITRATE STREAMING:
        - Multiple quality variants for different connection speeds
        - 1080p (10000k) - High quality for fast connections (original quality)
        - 720p (5000k) - Medium quality for average connections
        - 480p (2000k) - Low quality for slow connections
        - Master playlist for automatic quality selection
        - HLS.js will auto-select best quality based on bandwidth
        
        OPTIMIZED SETTINGS:
        - Framerate: 25 fps (lower CPU usage, smaller segments)
        - Segment duration: 2 seconds (faster start, smaller segments)
        - Container: fMP4 (better than MPEG-TS for modern browsers)
        """
        try:
            logger.info("Starting continuous HLS streaming with adaptive bitrate...")

            # Create HLS directory
            if not self.hls_dir or not os.path.exists(self.hls_dir):
                self.hls_dir = tempfile.mkdtemp(prefix="mbetterc_hls_")
                logger.info(f"Created HLS directory: {self.hls_dir}")
            
            # Master playlist path
            master_playlist = os.path.join(self.hls_dir, f"{self.stream_name}.m3u8")
            
            # Variant playlists and segments
            # High quality (1080p)
            hls_playlist_high = os.path.join(self.hls_dir, f"{self.stream_name}_high.m3u8")
            hls_segment_high = os.path.join(self.hls_dir, f"{self.stream_name}_high_%04d.m4s")
            hls_init_high = f"{self.stream_name}_high_init.mp4"
            
            # Medium quality (720p)
            hls_playlist_mid = os.path.join(self.hls_dir, f"{self.stream_name}_mid.m3u8")
            hls_segment_mid = os.path.join(self.hls_dir, f"{self.stream_name}_mid_%04d.m4s")
            hls_init_mid = f"{self.stream_name}_mid_init.mp4"
            
            # Low quality (480p)
            hls_playlist_low = os.path.join(self.hls_dir, f"{self.stream_name}_low.m3u8")
            hls_segment_low = os.path.join(self.hls_dir, f"{self.stream_name}_low_%04d.m4s")
            hls_init_low = f"{self.stream_name}_low_init.mp4"

            # ADAPTIVE BITRATE FFmpeg command
            # Creates 3 quality variants with a master playlist
            # HLS.js will automatically select the best quality
            cmd = [
                'ffmpeg',
                '-y',  # Overwrite output
                # ===== INPUT #0: rawvideo from stdin =====
                '-f', 'rawvideo',
                '-vcodec', 'rawvideo',
                '-pix_fmt', 'rgb24',
                '-s', '1920x1080',
                '-r', '25',  # 25 fps
                '-i', '-',  # Read from stdin
                # ===== INPUT #1: silent audio =====
                '-f', 'lavfi',
                '-i', 'anullsrc=r=44100:cl=stereo',
                # ===== Complex filter for scaling =====
                # Create 3 video streams at different resolutions
                '-filter_complex',
                '[0:v]split=3[vhigh][vmid][vlow];'
                '[vhigh]scale=1920:1080[vhighout];'
                '[vmid]scale=1280:720[vmidout];'
                '[vlow]scale=854:480[vlowout]',
                # ===== OUTPUT #0: High quality (1080p) =====
                '-map', '[vhighout]',
                '-map', '1:a',
                '-c:v:0', 'libx264',
                '-preset:0', 'veryfast',
                '-tune:0', 'zerolatency',
                '-g:0', '50',
                '-keyint_min:0', '50',
                '-sc_threshold:0', '0',
                '-pix_fmt:0', 'yuv420p',
                '-b:v:0', f'{self.bitrate_high}k',
                '-maxrate:0', f'{self.maxrate_high}k',
                '-bufsize:0', f'{self.bufsize_high}k',
                '-crf:0', '18',
                '-c:a:0', 'aac',
                '-b:a:0', '96k',
                '-ar:0', '44100',
                # ===== OUTPUT #1: Medium quality (720p) =====
                '-map', '[vmidout]',
                '-map', '1:a',
                '-c:v:1', 'libx264',
                '-preset:1', 'veryfast',
                '-tune:1', 'zerolatency',
                '-g:1', '50',
                '-keyint_min:1', '50',
                '-sc_threshold:1', '0',
                '-pix_fmt:1', 'yuv420p',
                '-b:v:1', f'{self.bitrate_medium}k',
                '-maxrate:1', f'{self.maxrate_medium}k',
                '-bufsize:1', f'{self.bufsize_medium}k',
                '-crf:1', '20',
                '-c:a:1', 'aac',
                '-b:a:1', '64k',
                '-ar:1', '44100',
                # ===== OUTPUT #2: Low quality (480p) =====
                '-map', '[vlowout]',
                '-map', '1:a',
                '-c:v:2', 'libx264',
                '-preset:2', 'veryfast',
                '-tune:2', 'zerolatency',
                '-g:2', '50',
                '-keyint_min:2', '50',
                '-sc_threshold:2', '0',
                '-pix_fmt:2', 'yuv420p',
                '-b:v:2', f'{self.bitrate_low}k',
                '-maxrate:2', f'{self.maxrate_low}k',
                '-bufsize:2', f'{self.bufsize_low}k',
                '-crf:2', '23',
                '-c:a:2', 'aac',
                '-b:a:2', '48k',
                '-ar:2', '44100',
                # ===== HLS OUTPUT with master playlist =====
                '-f', 'hls',
                '-hls_time', '2',
                '-hls_list_size', '20',
                '-hls_flags', 'independent_segments+delete_segments',
                '-hls_segment_type', 'fmp4',
                # Master playlist settings
                '-master_pl_name', os.path.basename(master_playlist),
                '-var_stream_map', 'v:0,a:0,name:high v:1,a:1,name:mid v:2,a:2,name:low',
                # Output pattern for variant playlists
                '-hls_segment_filename', f'{self.hls_dir}/%v_%04d.m4s',
                '-hls_fmp4_init_filename', '%v_init.mp4',
                '-start_number', '0',
                '-hls_allow_cache', '0',
                f'{self.hls_dir}/%v.m3u8'
            ]

            logger.info(f"Starting FFmpeg with adaptive bitrate command: {' '.join(cmd)}")

            self.ffmpeg_process = subprocess.Popen(
                cmd,
                stdin=subprocess.PIPE,  # stdin for video input from video feeder
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )
            logger.info(f"FFmpeg process started, PID: {self.ffmpeg_process.pid}")
            
            # Start thread to read FFmpeg stderr for debugging
            self.ffmpeg_stderr_thread = threading.Thread(target=self._read_ffmpeg_stderr, daemon=True)
            self.ffmpeg_stderr_thread.start()

            # Start video feeder process
            self.video_feeder = VideoFeederProcess(self.video_feeder_script)
            self.video_feeder.on_status = self._handle_video_feeder_status
            
            if not self.video_feeder.start(self.ffmpeg_process.stdin):
                logger.error("Failed to start video feeder process")
                return False

            # Wait for processes to initialize
            time.sleep(2)

            # Check if processes are running
            if self.ffmpeg_process.poll() is not None:
                logger.error("FFmpeg process failed to start")
                stderr = self.ffmpeg_process.stderr.read().decode('utf-8', errors='ignore')
                logger.error(f"FFmpeg stderr: {stderr}")
                return False

            if not self.video_feeder.is_running():
                logger.error("Video feeder process failed to start")
                return False

            self.is_streaming = True
            self.current_video_type = 'black_screen'
            logger.info("Continuous HLS streaming started successfully")
            return True

        except Exception as e:
            logger.error(f"Failed to start continuous HLS streaming: {e}")
            return False

    def _handle_video_feeder_status(self, status: Dict[str, Any]):
        """Handle status messages from video feeder process"""
        try:
            status_type = status.get("status")
            
            if status_type == "playing":
                logger.info(f"Video feeder playing: {status.get('video')}")
                self.last_frame_activity = time.time()
                self.video_feeder_healthy = True
                self.consecutive_stalled_count = 0
                
            elif status_type == "paused":
                logger.info("Video feeder paused")
                
            elif status_type == "ended":
                logger.info(f"Video ended: {status.get('video')}")
                # Handle video completion
                if not self._switching_video:
                    self._handle_video_completion()
                    
            elif status_type == "error":
                logger.error(f"Video feeder error: {status.get('message')}")
                self.video_feeder_healthy = False
                
            elif status_type == "ready":
                logger.info("Video feeder ready")
                self.last_frame_activity = time.time()
                
            elif status_type == "writing":
                # Frame writer is active - update activity timestamp
                self.last_frame_activity = time.time()
                is_healthy = status.get("is_healthy", True)
                self.video_feeder_healthy = is_healthy
                
                if not is_healthy:
                    logger.warning(f"Video feeder reports unhealthy: {status}")
                    
            elif status_type == "stalled":
                # Video feeder detected stall condition
                logger.warning(f"Video feeder STALLED: {status}")
                self.video_feeder_healthy = False
                self._handle_stalled_feeder(status)
                
            elif status_type == "reading":
                # Frame reader is active
                self.last_frame_activity = time.time()
                
        except Exception as e:
            logger.error(f"Error handling video feeder status: {e}")
    
    def _perform_health_check(self):
        """Perform periodic health check on streaming processes"""
        try:
            current_time = time.time()
            time_since_activity = current_time - self.last_frame_activity
            
            # Check if we've been inactive too long
            if time_since_activity > self.stalled_threshold:
                logger.warning(f"Health check: No frame activity for {time_since_activity:.1f}s (threshold: {self.stalled_threshold}s)")
                
                # Verify processes are actually running
                ffmpeg_running = self.ffmpeg_process and self.ffmpeg_process.poll() is None
                feeder_running = self.video_feeder and self.video_feeder.is_running()
                
                if not ffmpeg_running or not feeder_running:
                    logger.error(f"Health check: Process not running - FFmpeg: {ffmpeg_running}, Feeder: {feeder_running}")
                    self._handle_stalled_stream("process_not_running")
                elif not self.video_feeder_healthy:
                    logger.error("Health check: Video feeder reports unhealthy")
                    self._handle_stalled_stream("feeder_unhealthy")
                else:
                    # Processes running but no activity - might be stuck
                    logger.warning("Health check: Processes running but no frame activity")
                    self._handle_stalled_stream("no_activity")
            else:
                # Reset stalled count on healthy check
                self.consecutive_stalled_count = 0
                
                # Log health status periodically
                if self.debug_player:
                    logger.debug(f"Health check: OK (last activity {time_since_activity:.1f}s ago)")
                    
        except Exception as e:
            logger.error(f"Error in health check: {e}")
    
    def _handle_stalled_feeder(self, status: Dict[str, Any]):
        """Handle stalled video feeder detection"""
        try:
            reason = status.get("reason", "unknown")
            logger.warning(f"Handling stalled feeder - reason: {reason}")
            
            self.consecutive_stalled_count += 1
            logger.warning(f"Stalled count: {self.consecutive_stalled_count}/{self.max_stalled_count}")
            
            if self.consecutive_stalled_count >= self.max_stalled_count:
                logger.error("Max stalled count reached - triggering restart")
                self._restart_stalled_stream(f"feeder_stalled:{reason}")
            else:
                # Try to recover by requesting stats
                if self.video_feeder:
                    self.video_feeder.send_command({"command": "stats"})
                    
        except Exception as e:
            logger.error(f"Error handling stalled feeder: {e}")
    
    def _handle_stalled_stream(self, reason: str):
        """Handle stalled stream detection"""
        try:
            logger.warning(f"Stalled stream detected: {reason}")
            
            self.consecutive_stalled_count += 1
            logger.warning(f"Stalled count: {self.consecutive_stalled_count}/{self.max_stalled_count}")
            
            if self.consecutive_stalled_count >= self.max_stalled_count and self.auto_restart_enabled:
                logger.error("Max stalled count reached - restarting streaming")
                self._restart_stalled_stream(reason)
            else:
                # Try to get more info before restarting
                if self.video_feeder:
                    self.video_feeder.send_command({"command": "stats"})
                    
        except Exception as e:
            logger.error(f"Error handling stalled stream: {e}")
    
    def _restart_stalled_stream(self, reason: str):
        """Restart a stalled stream with full cleanup"""
        try:
            logger.info(f"Restarting stalled stream - reason: {reason}")
            
            # Reset stalled count
            self.consecutive_stalled_count = 0
            self.video_feeder_healthy = True
            self.last_frame_activity = time.time()
            
            # Stop everything
            if self.video_feeder:
                self.video_feeder.stop_process()
            
            self._stop_ffmpeg_process()
            
            # Wait for cleanup
            time.sleep(2)
            
            # Restart streaming
            if self._start_continuous_streaming():
                logger.info("Stalled stream restarted successfully")
                
                # Resume with current video or black screen
                if self.current_video_path and self.current_video_looping:
                    self.video_feeder.play(self.current_video_path, loop=True)
                else:
                    self._switch_to_black_screen()
            else:
                logger.error("Failed to restart stalled stream")
                
        except Exception as e:
            logger.error(f"Error restarting stalled stream: {e}")

    def _read_ffmpeg_stderr(self):
        """Thread to read FFmpeg stderr for debugging and monitoring"""
        # Use is_streaming flag instead of running, since running is set AFTER initialize()
        while self.is_streaming and self.ffmpeg_process:
            try:
                # Use select for non-blocking read with timeout
                import select
                # Check if there's data to read with 0.5 second timeout
                ready, _, _ = select.select([self.ffmpeg_process.stderr], [], [], 0.5)
                if not ready:
                    # No data available, check if process is still alive
                    if self.ffmpeg_process.poll() is not None:
                        logger.info("FFmpeg process ended")
                        break
                    continue
                
                line = self.ffmpeg_process.stderr.readline()
                if not line:
                    if self.ffmpeg_process.poll() is not None:
                        logger.info("FFmpeg stderr closed")
                        break
                    continue
                
                # Decode and log FFmpeg output
                try:
                    line_str = line.decode('utf-8', errors='ignore').strip()
                    if line_str:
                        # Log important FFmpeg messages
                        if any(keyword in line_str.lower() for keyword in ['error', 'warning', 'failed', 'invalid']):
                            logger.warning(f"FFmpeg: {line_str}")
                        elif self.debug_player:
                            logger.debug(f"FFmpeg: {line_str}")
                except Exception:
                    pass
                    
            except Exception as e:
                if self.is_streaming:
                    logger.error(f"Error reading FFmpeg stderr: {e}")
                time.sleep(0.1)
        
        logger.info("FFmpeg stderr thread ended")

    def _stop_ffmpeg_process(self):
        """Stop the FFmpeg process"""
        try:
            if self.ffmpeg_process:
                logger.info("Stopping FFmpeg process...")
                self.ffmpeg_process.terminate()
                try:
                    self.ffmpeg_process.wait(timeout=5)
                except subprocess.TimeoutExpired:
                    self.ffmpeg_process.kill()
                    self.ffmpeg_process.wait()
                self.ffmpeg_process = None
                logger.info("FFmpeg process stopped")
        except Exception as e:
            logger.error(f"Error stopping FFmpeg process: {e}")

    def _restart_streaming(self):
        """Restart streaming after failure"""
        logger.info("Restarting streaming...")
        
        # Stop video feeder
        if self.video_feeder:
            self.video_feeder.stop_process()
        
        # Stop FFmpeg
        self._stop_ffmpeg_process()
        
        time.sleep(1)
        
        if self._start_continuous_streaming():
            # Resume with current video or black screen
            if self.current_video_path and self.current_video_looping:
                self.video_feeder.play(self.current_video_path, loop=True)
            else:
                self._switch_to_black_screen()
        else:
            logger.error("Failed to restart streaming")

    def _switch_to_video(self, video_path: str, loop: bool = False, video_type: str = None):
        """Switch to a new video
        
        Args:
            video_path: Path to the video file
            loop: Whether to loop the video infinitely
            video_type: Type of video ('intro', 'match', 'result', or None for auto-detect)
        """
        try:
            logger.info(f"Switching to video: {video_path} (loop={loop})")
            
            self._switching_video = True

            # Update video type and phase
            if video_type:
                self.current_video_type = video_type
            elif 'intro' in video_path.lower() or 'INTRO' in video_path:
                self.current_video_type = 'intro'
            else:
                self.current_video_type = 'video'

            # Update video phase state machine
            if self.current_video_type == 'intro':
                self.video_phase = 'intro'
            elif self.current_video_type == 'match':
                self.video_phase = 'match'
            elif self.current_video_type == 'result':
                self.video_phase = 'result'
            else:
                self.video_phase = 'video'

            # Cancel any existing video end timer
            if self.video_end_timer:
                self.video_end_timer.cancel()
                self.video_end_timer = None

            # Set new video path
            self.current_video_path = video_path
            self.current_video_looping = loop

            # Get video duration
            duration = self._get_video_duration(video_path)
            self.current_video_duration = duration
            self.video_start_time = time.time()
            
            # Update phase tracking for HLS synchronization
            self.phase_start_time = time.time()
            self.phase_video_duration = duration

            # Tell video feeder to play the video
            if self.video_feeder:
                self.video_feeder.play(video_path, loop=loop)

            # Set up video completion timer if not looping
            if not loop and duration > 0:
                timer_delay = duration + 1.0
                logger.info(f"Setting video completion timer for {timer_delay:.2f} seconds")
                self.video_end_timer = threading.Timer(timer_delay, self._on_video_end_timer_expired)
                self.video_end_timer.daemon = True
                self.video_end_timer.start()

            logger.info(f"Video switch completed - phase: {self.video_phase}, duration: {duration:.2f}s")
            self._switching_video = False
            return True

        except Exception as e:
            logger.error(f"Failed to switch to video: {e}")
            self._switching_video = False
            return False

    def _switch_to_black_screen(self):
        """Switch to black screen - stop the video feeder"""
        try:
            logger.info("Switching to black screen")
            
            self.current_video_type = 'black_screen'
            self.current_video_looping = True
            
            # Update phase to idle when showing black screen
            self.video_phase = 'idle'
            self.phase_start_time = time.time()
            self.phase_video_duration = 0

            # Stop video feeder - it will output black frames
            if self.video_feeder:
                self.video_feeder.stop()

            # Cancel any video end timer
            if self.video_end_timer:
                self.video_end_timer.cancel()
                self.video_end_timer = None

            logger.info("Switched to black screen - phase: idle")
            return True

        except Exception as e:
            logger.error(f"Failed to switch to black screen: {e}")
            return False

    def _start_http_server(self):
        """Start HTTP server to serve HLS files"""
        try:
            if self.http_server:
                return

            class HLSRequestHandler(http.server.SimpleHTTPRequestHandler):
                def __init__(self, *args, streamer=None, **kwargs):
                    self.streamer = streamer
                    super().__init__(*args, **kwargs)

                def translate_path(self, path):
                    if self.streamer and self.streamer.hls_dir:
                        path = path.lstrip('/')
                        file_path = os.path.join(self.streamer.hls_dir, path)
                        if os.path.exists(file_path) and os.path.isfile(file_path):
                            return file_path
                        return "/nonexistent_file_404"
                    return "/nonexistent_file_404"

                def do_GET(self):
                    try:
                        if not self.streamer or not self.streamer.hls_dir:
                            self.send_error(404, "No active stream")
                            return
                        file_path = self.translate_path(self.path)
                        if not os.path.exists(file_path):
                            self.send_error(404, "File not found")
                            return
                        super().do_GET()
                    except (ConnectionResetError, BrokenPipeError, OSError):
                        pass

                def log_message(self, format, *args):
                    pass

            self.http_server = socketserver.TCPServer(("", self.http_port), 
                lambda *args, **kwargs: HLSRequestHandler(*args, streamer=self, **kwargs))
            
            server_thread = threading.Thread(target=self.http_server.serve_forever, daemon=True)
            server_thread.start()
            logger.info(f"HTTP server started on port {self.http_port}")

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

    def _stop_http_server(self):
        """Stop HTTP server"""
        try:
            if self.http_server:
                self.http_server.shutdown()
                self.http_server.server_close()
                self.http_server = None
                logger.info("HTTP server stopped")
        except Exception as e:
            logger.error(f"Error stopping HTTP server: {e}")

    def _update_overlay(self, data: Dict[str, Any]):
        """Update overlay data"""
        self.overlay_data.update(data)
        
        # Add video phase state to overlay data for web player synchronization
        self.overlay_data['video_phase'] = self.video_phase
        self.overlay_data['phase_start_time'] = self.phase_start_time
        self.overlay_data['phase_video_duration'] = self.phase_video_duration
        self.overlay_data['hls_delay_compensation'] = self.hls_delay_compensation
        
        logger.debug(f"Overlay updated: {self.overlay_data}")

    def _process_message(self, message: Message):
        """Process incoming messages"""
        try:
            if self.debug_player:
                logger.info(f"Processing message: {message.type.value}")

            handlers = {
                MessageType.VIDEO_PLAY: self._handle_video_play,
                MessageType.VIDEO_PAUSE: self._handle_video_pause,
                MessageType.VIDEO_STOP: self._handle_video_stop,
                MessageType.VIDEO_SEEK: self._handle_video_seek,
                MessageType.VIDEO_VOLUME: self._handle_video_volume,
                MessageType.TEMPLATE_CHANGE: self._handle_template_change,
                MessageType.OVERLAY_UPDATE: self._handle_overlay_update,
                MessageType.START_INTRO: self._handle_start_intro,
                MessageType.PLAY_VIDEO_MATCH: self._handle_play_video_match,
                MessageType.PLAY_VIDEO_RESULT: self._handle_play_video_result,
                MessageType.GAME_STATUS: self._handle_game_status,
            }

            handler = handlers.get(message.type)
            if handler:
                handler(message)
            else:
                logger.debug(f"Unhandled message type: {message.type}")

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

    # Message handlers

    def _handle_video_play(self, message: Message):
        """Handle video play message"""
        file_path = message.data.get("file_path")
        if not file_path:
            logger.error("No file path provided")
            return

        logger.info(f"Playing video: {file_path}")
        template_data = message.data.get("overlay_data", {})
        if template_data:
            self._update_overlay(template_data)

        self._switch_to_video(file_path)

    def _handle_video_pause(self, message: Message):
        """Handle video pause message"""
        logger.info("Pausing video")
        if self.video_feeder:
            self.video_feeder.pause()

    def _handle_video_stop(self, message: Message):
        """Handle video stop message"""
        logger.info("Stopping video")
        self._switch_to_black_screen()

    def _handle_video_seek(self, message: Message):
        """Handle video seek message"""
        position = message.data.get("position", 0)
        logger.info(f"Seek to position {position} (not implemented)")

    def _handle_video_volume(self, message: Message):
        """Handle video volume message"""
        volume = message.data.get("volume", 100)
        logger.info(f"Volume change to {volume}% (not implemented)")

    def _handle_template_change(self, message: Message):
        """Handle template change message"""
        template_data = message.data.get("template_data", {})
        if template_data:
            self._update_overlay(template_data)

    def _handle_overlay_update(self, message: Message):
        """Handle overlay update message"""
        overlay_data = message.data.get("overlay_data", {})
        self._update_overlay(overlay_data)

    def _handle_start_intro(self, message: Message):
        """Handle START_INTRO message"""
        logger.info("Handling START_INTRO message")

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

        intro_path = self._find_intro_video_file(match_id)

        if intro_path:
            logger.info(f"Starting intro video: {intro_path}")

            self._update_overlay({
                "title": "Intro Video",
                "subtitle": "Preparing for match...",
                "match_id": match_id,
                "fixture_id": fixture_id
            })
            
            self.intro_video_path = str(intro_path)
            self._switch_to_video(str(intro_path), loop=True, video_type='intro')
        else:
            logger.warning("No intro video found")

    def _handle_play_video_match(self, message: Message):
        """Handle PLAY_VIDEO_MATCH message"""
        try:
            logger.info("Handling PLAY_VIDEO_MATCH message")

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

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

            # Check for OVER/UNDER sequence
            if video_filename.startswith("OVER_") or video_filename.startswith("UNDER_"):
                self._handle_over_under_sequence(match_id, video_filename, fixture_id, result)
                return

            match_video_path = self._find_match_video_file(match_id, video_filename)

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

                self.is_playing_match_video = True
                self.current_match_id = match_id
                self.current_match_video_filename = video_filename
                self.current_fixture_id = fixture_id
                self.current_result = result

                self._update_overlay({
                    "title": f"Match {match_id}",
                    "subtitle": f"Result: {result}" if result else "Live Action",
                    "result": result,
                    "match_id": match_id,
                    "fixture_id": fixture_id
                })

                self._switch_to_video(str(match_video_path), loop=False, video_type='match')
            else:
                logger.error(f"Match video not found: {video_filename}")

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

    def _handle_play_video_result(self, message: Message):
        """Handle PLAY_VIDEO_RESULT message"""
        try:
            logger.info("Handling PLAY_VIDEO_RESULT message")

            fixture_id = message.data.get("fixture_id")
            match_id = message.data.get("match_id")
            result = message.data.get("result")
            winning_outcomes = message.data.get("winning_outcomes", [])

            result_video_path = self._find_result_video_file(match_id, result)

            if result_video_path:
                logger.info(f"Switching to result video: {result_video_path}")

                if self.is_playing_match_video:
                    self.is_playing_match_video = False

                self._update_overlay({
                    'outcome': result,
                    'result': result,
                    'winningOutcomes': [{'outcome': o} for o in winning_outcomes],
                    'match_id': match_id,
                    'fixture_id': fixture_id,
                    'is_result_video': True
                })

                self._switch_to_video(str(result_video_path), loop=False, video_type='result')
            else:
                logger.error(f"Result video not found for {result}")

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

    def _handle_game_status(self, message: Message):
        """Handle GAME_STATUS message"""
        status = message.data.get("status")
        logger.info(f"Game status: {status}")

    def _start_test_intro(self):
        """Start intro video for testing"""
        try:
            logger.info("Starting test intro video")

            intro_message = Message(
                type=MessageType.START_INTRO,
                sender="test_mode",
                data={"match_id": None, "fixture_id": None}
            )

            self._handle_start_intro(intro_message)
        except Exception as e:
            logger.error(f"Failed to start test intro: {e}")

    # Helper methods

    def _find_intro_video_file(self, match_id: int) -> Optional[Path]:
        """Find intro video file"""
        try:
            assets_dir = Path(__file__).parent.parent.parent / "assets"
            intro_file = assets_dir / "INTRO.mp4"

            if intro_file.exists():
                logger.info(f"Found intro video: {intro_file}")
                return intro_file
            else:
                logger.warning(f"Intro video not found: {intro_file}")
                return None
        except Exception as e:
            logger.error(f"Failed to find intro video: {e}")
            return None

    def _find_match_video_file(self, match_id: int, video_filename: str) -> Optional[Path]:
        """Find match video file"""
        try:
            temp_base = Path(tempfile.gettempdir())
            temp_dir_pattern = f"match_{match_id}_"

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

            logger.warning(f"Match video not found: {video_filename}")
            return None
        except Exception as e:
            logger.error(f"Failed to find match video: {e}")
            return None

    def _find_result_video_file(self, match_id: int, result: str) -> Optional[Path]:
        """Find result video file"""
        try:
            temp_base = Path(tempfile.gettempdir())
            temp_dir_pattern = f"match_{match_id}_"
            result_filename = f"{result}.mp4"

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

            logger.warning(f"Result video not found: {result_filename}")
            return None
        except Exception as e:
            logger.error(f"Failed to find result video: {e}")
            return None

    def _get_video_duration(self, video_path: str) -> float:
        """Get video duration using ffprobe"""
        try:
            cmd = [
                'ffprobe', '-v', 'error',
                '-show_entries', 'format=duration',
                '-of', 'default=noprint_wrappers=1:nokey=1',
                str(video_path)
            ]

            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)

            if result.returncode == 0:
                return float(result.stdout.strip())
            return 0.0
        except Exception as e:
            logger.error(f"Failed to get video duration: {e}")
            return 0.0

    def _on_video_end_timer_expired(self):
        """Called when video completion timer expires"""
        logger.info(f"Video end timer expired for {self.current_video_type}")
        self._handle_video_completion()

    def _handle_video_completion(self):
        """Handle video completion"""
        try:
            logger.info(f"Handling video completion for type: {self.current_video_type}, phase: {self.video_phase}")

            if self.current_video_type == 'match':
                if self.current_match_id and self.current_match_video_filename:
                    logger.info(f"Sending PLAY_VIDEO_MATCH_DONE for match {self.current_match_id}")
                    done_message = MessageBuilder.play_video_match_done(
                        sender=self.name,
                        match_id=self.current_match_id,
                        video_filename=self.current_match_video_filename,
                        fixture_id=self.current_fixture_id
                    )
                    self.message_bus.publish(done_message, broadcast=True)
                    self.is_playing_match_video = False
                    
                    # Store result data for overlay before transitioning
                    self.result_data = {
                        'outcome': self.current_result,
                        'result': self.current_result,
                        'match_id': self.current_match_id,
                        'fixture_id': self.current_fixture_id
                    }

            elif self.current_video_type == 'result':
                # Switch to black screen first, then send PLAY_VIDEO_RESULT_DONE after 5 seconds
                # This matches the Qt player behavior
                logger.info(f"Result video completed - switching to black screen for 5 seconds before signaling done")
                self._switch_to_black_screen()
                
                # Schedule sending PLAY_VIDEO_RESULT_DONE message after 5 seconds (non-blocking)
                # This allows the results overlay template to show results before sending the done message
                if self.current_match_id and self.current_result:
                    result_info = {
                        'fixture_id': self.current_fixture_id,
                        'match_id': self.current_match_id,
                        'result': self.current_result
                    }
                    # Store result info for the delayed callback
                    self._pending_result_done_info = result_info
                    
                    # Create a timer to send the done message after 5 seconds
                    def send_result_done_after_delay():
                        try:
                            if hasattr(self, '_pending_result_done_info') and self._pending_result_done_info:
                                info = self._pending_result_done_info
                                logger.info(f"Sending PLAY_VIDEO_RESULT_DONE after 5 second delay")
                                done_message = MessageBuilder.play_video_result_done(
                                    sender=self.name,
                                    fixture_id=info['fixture_id'],
                                    match_id=info['match_id'],
                                    result=info['result']
                                )
                                self.message_bus.publish(done_message, broadcast=True)
                                self._pending_result_done_info = None
                                # Clear result data after result video completes
                                self.result_data = None
                        except Exception as e:
                            logger.error(f"Failed to send delayed result done message: {e}")
                    
                    # Schedule the callback
                    result_done_timer = threading.Timer(5.0, send_result_done_after_delay)
                    result_done_timer.daemon = True
                    result_done_timer.start()
                    logger.info(f"Scheduled PLAY_VIDEO_RESULT_DONE in 5 seconds")
                
                return  # Don't switch to black screen again at the end of the function

            elif self.current_video_type == 'intro':
                logger.warning("Intro video ended (should loop)")
                if self.intro_video_path:
                    self._switch_to_video(self.intro_video_path, loop=True, video_type='intro')

            # Switch to black screen after match/result
            if self.current_video_type in ['match', 'result']:
                logger.info(f"Switching to black screen after {self.current_video_type}")
                self._switch_to_black_screen()

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

    def _handle_over_under_sequence(self, match_id: int, video_filename: str, fixture_id: str, result: str):
        """Handle OVER/UNDER video sequence"""
        try:
            logger.info(f"Handling OVER/UNDER sequence for match {match_id}")

            if video_filename.startswith("OVER_"):
                over_video = video_filename
                under_video = video_filename.replace("OVER_", "UNDER_")
                self.over_under_sequence = [
                    {'type': 'match', 'filename': over_video, 'result': 'OVER'},
                    {'type': 'match', 'filename': under_video, 'result': 'UNDER'}
                ]
            else:
                under_video = video_filename
                over_video = video_filename.replace("UNDER_", "OVER_")
                self.over_under_sequence = [
                    {'type': 'match', 'filename': under_video, 'result': 'UNDER'},
                    {'type': 'match', 'filename': over_video, 'result': 'OVER'}
                ]

            self.current_over_under_index = 0
            self.is_playing_over_under = True
            self.current_match_id = match_id
            self.current_fixture_id = fixture_id
            self.current_result = result

            self._play_next_over_under_video()

        except Exception as e:
            logger.error(f"Failed to handle OVER/UNDER sequence: {e}")

    def _play_next_over_under_video(self):
        """Play next video in OVER/UNDER sequence"""
        try:
            if self.current_over_under_index >= len(self.over_under_sequence):
                logger.info("OVER/UNDER sequence completed")
                self.is_playing_over_under = False
                self._play_result_after_over_under()
                return

            video_info = self.over_under_sequence[self.current_over_under_index]
            video_filename = video_info['filename']
            video_result = video_info['result']

            logger.info(f"Playing OVER/UNDER video {self.current_over_under_index + 1}/{len(self.over_under_sequence)}")

            match_video_path = self._find_match_video_file(self.current_match_id, video_filename)

            if match_video_path:
                self._update_overlay({
                    "title": f"Match {self.current_match_id}",
                    "subtitle": f"Result: {video_result}",
                    "result": video_result,
                    "match_id": self.current_match_id,
                    "fixture_id": self.current_fixture_id
                })

                self._switch_to_video(str(match_video_path), loop=False, video_type='match')
                self.current_match_video_filename = video_filename
            else:
                logger.error(f"OVER/UNDER video not found: {video_filename}")
                self.current_over_under_index += 1
                self._play_next_over_under_video()

        except Exception as e:
            logger.error(f"Failed to play next OVER/UNDER video: {e}")

    def _play_result_after_over_under(self):
        """Play result video after OVER/UNDER sequence"""
        try:
            logger.info(f"Playing result video after OVER/UNDER")

            if self.current_match_id and self.current_match_video_filename:
                done_message = MessageBuilder.play_video_match_done(
                    sender=self.name,
                    match_id=self.current_match_id,
                    video_filename=self.current_match_video_filename,
                    fixture_id=self.current_fixture_id
                )
                self.message_bus.publish(done_message, broadcast=True)

            result_video_path = self._find_result_video_file(self.current_match_id, self.current_result)

            if result_video_path:
                self._update_overlay({
                    'outcome': self.current_result,
                    'result': self.current_result,
                    'match_id': self.current_match_id,
                    'fixture_id': self.current_fixture_id,
                    'is_result_video': True
                })

                self._switch_to_video(str(result_video_path), loop=False, video_type='result')
            else:
                logger.error(f"Result video not found for {self.current_result}")

        except Exception as e:
            logger.error(f"Failed to play result after OVER/UNDER: {e}")