/**
 * Web Overlay Controller for MbetterClient Web Player
 * 
 * This controller manages overlay templates for the web video player,
 * providing similar functionality to the Qt player's overlay system.
 * 
 * Communication methods:
 * - REST API for data fetching (replaces Qt WebChannel synchronous calls)
 * - WebSocket for real-time updates (replaces Qt WebChannel signals)
 * - postMessage for iframe communication (replaces Qt WebChannel within templates)
 */

class WebOverlayController {
    constructor(options = {}) {
        // Support both element references and element IDs
        if (options.videoElementId) {
            this.videoElement = document.getElementById(options.videoElementId);
        } else {
            this.videoElement = options.videoElement;
        }
        
        if (options.overlayContainerId) {
            this.overlayContainer = document.getElementById(options.overlayContainerId);
        } else {
            this.overlayContainer = options.overlayContainer;
        }
        
        // Get or create overlay iframe
        if (this.overlayContainer) {
            this.overlayIframe = this.overlayContainer.querySelector('iframe') ||
                                 document.getElementById('overlay-iframe');
        } else {
            this.overlayIframe = options.overlayIframe;
        }
        
        this.apiBaseUrl = options.apiBaseUrl || '/api/overlay';
        this.wsUrl = options.wsUrl || null;
        this.authToken = options.authToken || null;
        this.debug = options.debug || false;
        
        // State
        this.currentTemplate = null;
        this.currentTemplateIndex = 0;
        this.overlayData = {};
        this.fixtureData = null;
        this.timerState = { running: false, remaining_seconds: 0 };
        this.completedMatches = [];
        this.licenseText = '';
        this.isPaused = false;
        
        // Video Phase State Machine for HLS synchronization
        this.videoPhase = 'idle';  // 'idle', 'intro', 'match', 'result'
        this.phaseStartTime = 0;
        this.phaseVideoDuration = 0;
        this.hlsDelayCompensation = 5.0;  // Default 5 seconds delay compensation
        this.lastKnownPhase = 'idle';
        this.phaseTransitionPending = false;
        this.phaseTransitionTimer = null;  // Timer for delayed phase transitions
        
        // Template rotation
        this.templateSequence = [];
        this.rotatingTime = 15; // Default 15 seconds
        this.rotationTimer = null;
        this.rotationEnabled = false;
        
        // Polling
        this.pollingInterval = null;
        this.pollingFrequency = options.pollInterval || options.pollingFrequency || 1000; // 1 second default
        
        // WebSocket
        this.websocket = null;
        this.wsReconnectAttempts = 0;
        this.wsMaxReconnectAttempts = 5;
        this.wsReconnectDelay = 1000;
        
        // Video synchronization
        this.lastPositionUpdate = 0;
        this.positionUpdateInterval = 250; // Update overlay every 250ms
        
        // Overlay scaling (templates are designed for 1920x1080)
        this.templateBaseWidth = 1920;
        this.templateBaseHeight = 1080;
        this.currentScale = 1;
        this.resizeObserver = null;
        
        // Template triggers (time-based template changes)
        this.templateTriggers = [];
        
        // Event listeners
        this.eventListeners = {};
        
        // Don't auto-initialize - let the caller call initialize()
        this.initialized = false;
    }
    
    /**
     * Initialize the overlay controller
     */
    async initialize() {
        if (this.initialized) {
            this.log('Already initialized, skipping');
            return;
        }
        
        this.log('Starting initialization...');
        
        // Set up video event listeners for synchronization
        if (this.videoElement) {
            this.videoElement.addEventListener('timeupdate', this.onTimeUpdate.bind(this));
            this.videoElement.addEventListener('play', this.onPlay.bind(this));
            this.videoElement.addEventListener('pause', this.onPause.bind(this));
            this.videoElement.addEventListener('ended', this.onEnded.bind(this));
            this.videoElement.addEventListener('loadedmetadata', this.onLoadedMetadata.bind(this));
        }
        
        // Set up message listener for iframe communication
        window.addEventListener('message', this.onIframeMessage.bind(this));
        this.log('Message listener set up for iframe communication');
        
        // Set up resize observer for overlay scaling
        this.setupResizeObserver();
        
        // Initial scale calculation
        this.updateOverlayScale();
        
        // IMPORTANT: Load template configuration BEFORE starting real-time updates
        // This ensures we know the template sequence before handling phase transitions
        this.log('Loading template config...');
        await this.loadTemplateConfig();
        this.log('Template config loaded');
        
        // IMPORTANT: Fetch initial overlay data to check video phase BEFORE loading default template
        // This prevents loading the wrong template during match/result phases
        this.log('Fetching initial overlay data to check video phase...');
        await this.fetchOverlayData();
        this.log(`Initial video phase: ${this.videoPhase}`);
        
        // Load appropriate template based on current video phase
        if (this.videoPhase === 'match') {
            this.log('In match phase - loading match_video template');
            await this.loadTemplate('match_video');
        } else if (this.videoPhase === 'result') {
            this.log('In result phase - loading results template');
            await this.loadTemplate('results');
        } else {
            // Load default template for idle/intro phases
            this.log('Loading default template...');
            await this.loadTemplate('default');
            this.log('Default template loaded');
            
            // Start template rotation only if in idle/intro phase
            if (this.videoPhase === 'idle' || this.videoPhase === 'intro') {
                this.startTemplateRotation();
            }
        }
        
        this.initialized = true;
        this.log('Initialization complete');
        
        // Start real-time updates (polling for phase changes)
        this.startRealTimeUpdates();
        
        // Emit initialized event
        this.emit('initialized');
    }
    
    /**
     * Load template configuration from API
     */
    async loadTemplateConfig() {
        this.log('loadTemplateConfig() called');
        try {
            this.log('Loading template config...');
            const config = await this.apiRequest('/template-config');
            this.log('Template config response:', config);
            
            if (config && config.templates && config.templates.length > 0) {
                this.templateSequence = config.templates;
                
                // Parse rotating time (format: "MM:SS")
                if (config.rotating_time) {
                    const parts = config.rotating_time.split(':');
                    if (parts.length === 2) {
                        const minutes = parseInt(parts[0], 10);
                        const seconds = parseInt(parts[1], 10);
                        this.rotatingTime = minutes * 60 + seconds;
                    }
                }
                
                this.log(`Loaded template config: ${this.templateSequence.length} templates, rotating every ${this.rotatingTime}s`);
                this.log('Template sequence:', this.templateSequence.map(t => t.name).join(', '));
                
                // NOTE: Don't start template rotation here - it will be started
                // conditionally in initialize() based on the current video phase
            } else {
                this.log('No template sequence configured, using default template');
            }
        } catch (error) {
            this.log('Failed to load template config:', error.message);
        }
    }
    
    /**
     * Start template rotation timer
     */
    startTemplateRotation() {
        // Don't start rotation if in match or result phase
        if (this.videoPhase === 'match' || this.videoPhase === 'result') {
            this.log(`Template rotation not started - in ${this.videoPhase} phase`);
            return;
        }
        
        if (!this.templateSequence || this.templateSequence.length <= 1) {
            this.log('Template rotation disabled: need at least 2 templates');
            return;
        }
        
        this.rotationEnabled = true;
        
        // Stop any existing rotation timer
        if (this.rotationTimer) {
            clearInterval(this.rotationTimer);
            this.rotationTimer = null;
        }
        
        // Load first template immediately
        this.currentTemplateIndex = 0;
        const firstTemplate = this.templateSequence[0].name;
        this.loadTemplate(firstTemplate);
        
        // Start rotation timer
        this.rotationTimer = setInterval(() => {
            this.rotateTemplate();
        }, this.rotatingTime * 1000);
        
        this.log(`Template rotation started: ${this.rotatingTime}s interval`);
    }
    
    /**
     * Stop template rotation timer
     */
    stopTemplateRotation() {
        if (this.rotationTimer) {
            clearInterval(this.rotationTimer);
            this.rotationTimer = null;
        }
        this.rotationEnabled = false;
        this.log('Template rotation stopped');
    }
    
    /**
     * Rotate to the next template in the sequence
     */
    rotateTemplate() {
        // Don't rotate if in match or result phase
        if (this.videoPhase === 'match' || this.videoPhase === 'result') {
            this.log(`Template rotation skipped - in ${this.videoPhase} phase`);
            return;
        }
        
        if (!this.templateSequence || this.templateSequence.length === 0) {
            this.log('No template sequence available for rotation');
            return;
        }
        
        // Move to next template
        this.currentTemplateIndex = (this.currentTemplateIndex + 1) % this.templateSequence.length;
        const nextTemplate = this.templateSequence[this.currentTemplateIndex].name;
        
        this.log(`Rotating to template: ${nextTemplate} (index ${this.currentTemplateIndex})`);
        
        // Load the new template
        this.loadTemplate(nextTemplate);
    }
    
    /**
     * Set template sequence and start rotation
     * @param {Array} templates - Array of template objects with 'name' and 'show_time'
     * @param {string} rotatingTime - Rotation interval in "MM:SS" format
     */
    setTemplateSequence(templates, rotatingTime = '00:15') {
        this.templateSequence = templates;
        
        // Parse rotating time
        const parts = rotatingTime.split(':');
        if (parts.length === 2) {
            const minutes = parseInt(parts[0], 10);
            const seconds = parseInt(parts[1], 10);
            this.rotatingTime = minutes * 60 + seconds;
        }
        
        this.log(`Template sequence updated: ${templates.length} templates, rotating every ${this.rotatingTime}s`);
        
        // Restart rotation with new sequence
        this.startTemplateRotation();
    }
    
    /**
     * Load a specific template by index
     * @param {number} index - Index in the template sequence
     */
    loadTemplateByIndex(index) {
        if (!this.templateSequence || index < 0 || index >= this.templateSequence.length) {
            this.log(`Invalid template index: ${index}`);
            return;
        }
        
        this.currentTemplateIndex = index;
        const template = this.templateSequence[index].name;
        this.loadTemplate(template);
    }
    
    /**
     * Register an event listener
     * @param {string} event - Event name
     * @param {Function} callback - Callback function
     */
    on(event, callback) {
        if (!this.eventListeners[event]) {
            this.eventListeners[event] = [];
        }
        this.eventListeners[event].push(callback);
    }
    
    /**
     * Emit an event
     * @param {string} event - Event name
     * @param {*} data - Event data
     */
    emit(event, data) {
        if (this.eventListeners[event]) {
            this.eventListeners[event].forEach(callback => {
                try {
                    callback(data);
                } catch (error) {
                    console.error(`[OverlayController] Error in event listener for ${event}:`, error);
                }
            });
        }
    }
    
    /**
     * Log message if debug mode is enabled
     * @param {...*} args - Arguments to log
     */
    log(...args) {
        if (this.debug) {
            console.log('[OverlayController]', ...args);
        }
    }
    
    
    /**
     * Load an overlay template
     * @param {string} templateName - Name of the template (e.g., 'fixtures', 'results', 'match')
     */
    async loadTemplate(templateName) {
        // Normalize template name
        const normalizedName = templateName.replace('.html', '').toLowerCase();
        
        // Guard: Skip if already loading or already showing this template
        if (this.currentTemplate === normalizedName) {
            this.log(`Template "${normalizedName}" already loaded, skipping`);
            return true;
        }
        
        this.log(`Loading template: ${templateName} (current: ${this.currentTemplate})`);
        
        try {
            // Map template names to template paths
            const templatePath = this.getTemplatePath(templateName);
            
            if (!templatePath) {
                console.error(`[OverlayController] Unknown template: ${templateName}`);
                return false;
            }
            
            // Load template into iframe
            if (this.overlayIframe) {
                // Set up one-time load handler to send initial data
                const sendInitialData = () => {
                    this.log('Template iframe loaded, sending initial data');
                    // Send initial data after template loads
                    setTimeout(async () => {
                        // Fetch and send fixture data
                        const fixtureData = await this.fetchFixtureData();
                        if (fixtureData) {
                            this.sendMessageToOverlay('fixtureData', { fixtures: fixtureData });
                        }
                        
                        // Fetch and send timer state
                        const timerState = await this.fetchTimerState();
                        this.sendMessageToOverlay('timerState', timerState);
                        
                        // Fetch and send license text
                        const licenseText = await this.fetchLicenseText();
                        this.sendMessageToOverlay('licenseText', { licenseText });
                        
                        // Fetch and send completed matches
                        const completedMatches = await this.fetchCompletedMatches();
                        this.sendMessageToOverlay('completedMatches', { matches: completedMatches });
                        
                        // Send current overlay data
                        if (this.overlayData) {
                            this.sendMessageToOverlay('initialData', {
                                overlayData: this.overlayData,
                                timerState: this.timerState,
                                licenseText: this.licenseText
                            });
                        }
                        
                        // CRITICAL: For results template, send the stored result data
                        // This ensures the template gets the data AFTER it loads
                        if (normalizedName === 'results' && this.resultData) {
                            this.log('Sending stored result data to results template:', this.resultData);
                            // Send as resultUpdate first (primary method)
                            this.sendMessageToOverlay('resultUpdate', this.resultData);
                            // Also send as dataUpdated for compatibility
                            this.sendMessageToOverlay('dataUpdated', this.resultData);
                        }
                        
                        // CRITICAL: For match_video template, send the stored match data
                        // This ensures the template gets the data AFTER it loads
                        if (normalizedName === 'match_video' && this.matchData) {
                            this.log('Sending stored match data to match_video template:', this.matchData);
                            // Send as matchUpdate first (primary method)
                            this.sendMessageToOverlay('matchUpdate', this.matchData);
                            // Also send as dataUpdated for compatibility
                            this.sendMessageToOverlay('dataUpdated', this.matchData);
                        }
                    }, 100); // Small delay to ensure iframe is ready
                };
                
                // Remove any existing load listener
                this.overlayIframe.removeEventListener('load', this._onTemplateLoad);
                
                // Add load listener
                this._onTemplateLoad = sendInitialData;
                this.overlayIframe.addEventListener('load', sendInitialData);
                
                // Load the template
                this.overlayIframe.src = templatePath;
                this.currentTemplate = normalizedName;  // Store normalized name
                this.log(`Template loaded: ${templatePath}`);
                
                // Emit template loaded event
                this.emit('templateLoaded', templateName);
            }
            
            return true;
        } catch (error) {
            console.error('[OverlayController] Failed to load template:', error);
            this.emit('error', error);
            return false;
        }
    }
    
    /**
     * Get the template path for a given template name
     * @param {string} templateName - Name of the template
     * @returns {string} - Path to the template
     */
    getTemplatePath(templateName) {
        // Templates are symlinked at web/templates -> ../mbetterclient/qt_player/templates
        // Nginx root is /home/mbetterclient/web, so /templates/ serves the symlink
        const templateMap = {
            'default': '/templates/default.html',
            'fixtures': '/templates/fixtures.html',
            'match': '/templates/match.html',
            'results': '/templates/results.html',
            'text': '/templates/text.html',
            'match_video': '/templates/match_video.html'
        };
        
        // Normalize template name
        const normalizedName = templateName.replace('.html', '').toLowerCase();
        
        return templateMap[normalizedName] || templateMap['default'];
    }
    
    /**
     * Start real-time updates via polling or WebSocket
     */
    startRealTimeUpdates() {
        // Try WebSocket first for real-time updates
        if (this.wsUrl) {
            this.connectWebSocket();
        }
        
        // Always start polling as fallback/supplement
        this.startPolling();
    }
    
    /**
     * Start polling for overlay data
     */
    startPolling() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
        }
        
        this.log(`Starting polling with frequency: ${this.pollingFrequency}ms`);
        
        this.pollingInterval = setInterval(async () => {
            try {
                await this.fetchOverlayData();
            } catch (error) {
                console.error('[OverlayController] Polling error:', error);
            }
        }, this.pollingFrequency);
        
        // Fetch initial data immediately
        this.fetchOverlayData();
    }
    
    /**
     * Stop polling for overlay data
     */
    stopPolling() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
            this.pollingInterval = null;
        }
    }
    
    /**
     * Connect to WebSocket for real-time updates
     */
    connectWebSocket() {
        if (!this.wsUrl) {
            this.log('No WebSocket URL configured, using polling only');
            return;
        }
        
        try {
            this.log(`Connecting to WebSocket: ${this.wsUrl}`);
            this.websocket = new WebSocket(this.wsUrl);
            
            this.websocket.onopen = () => {
                this.log('WebSocket connected');
                this.wsReconnectAttempts = 0;
                
                // Send authentication
                if (this.authToken) {
                    this.websocket.send(JSON.stringify({
                        type: 'auth',
                        token: this.authToken
                    }));
                }
            };
            
            this.websocket.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    this.handleWebSocketMessage(data);
                } catch (error) {
                    console.error('[OverlayController] WebSocket message parse error:', error);
                }
            };
            
            this.websocket.onclose = (event) => {
                this.log('WebSocket closed:', event.code, event.reason);
                this.scheduleWebSocketReconnect();
            };
            
            this.websocket.onerror = (error) => {
                console.error('[OverlayController] WebSocket error:', error);
            };
            
        } catch (error) {
            console.error('[OverlayController] WebSocket connection failed:', error);
            this.scheduleWebSocketReconnect();
        }
    }
    
    /**
     * Schedule WebSocket reconnection with exponential backoff
     */
    scheduleWebSocketReconnect() {
        if (this.wsReconnectAttempts >= this.wsMaxReconnectAttempts) {
            this.log('Max WebSocket reconnect attempts reached, using polling only');
            return;
        }
        
        this.wsReconnectAttempts++;
        const delay = this.wsReconnectDelay * Math.pow(2, this.wsReconnectAttempts - 1);
        
        this.log(`Scheduling WebSocket reconnect in ${delay}ms (attempt ${this.wsReconnectAttempts})`);
        
        setTimeout(() => {
            this.connectWebSocket();
        }, delay);
    }
    
    /**
     * Handle WebSocket messages
     * @param {Object} data - WebSocket message data
     */
    handleWebSocketMessage(data) {
        this.log('WebSocket message received:', data.type);
        
        switch (data.type) {
            case 'overlayUpdate':
            case 'dataUpdated':
                this.overlayData = { ...this.overlayData, ...data.payload };
                this.sendMessageToOverlay('dataUpdated', data.payload);
                break;
                
            case 'templateChange':
                this.loadTemplate(data.template);
                break;
                
            case 'timerUpdate':
                this.timerState = data.payload;
                this.sendMessageToOverlay('timerUpdate', data.payload);
                break;
                
            case 'matchUpdate':
                this.sendMessageToOverlay('matchUpdate', data.payload);
                break;
                
            case 'resultUpdate':
                this.sendMessageToOverlay('resultUpdate', data.payload);
                break;
                
            default:
                this.log('Unknown WebSocket message type:', data.type);
        }
    }
    
    /**
     * Fetch overlay data from REST API
     * Uses Video Phase State Machine for HLS delay compensation
     */
    async fetchOverlayData() {
        try {
            const response = await this.apiRequest('/data');
            
            if (response) {
                const hasChanges = JSON.stringify(this.overlayData) !== JSON.stringify(response);
                
                // Extract video phase state from response
                const newPhase = response.video_phase || 'idle';
                const newPhaseStartTime = response.phase_start_time || 0;
                const newPhaseVideoDuration = response.phase_video_duration || 0;
                const newHlsDelayCompensation = response.hls_delay_compensation || 5.0;
                
                // Update HLS delay compensation if changed
                if (newHlsDelayCompensation !== this.hlsDelayCompensation) {
                    this.hlsDelayCompensation = newHlsDelayCompensation;
                    this.log(`HLS delay compensation updated to ${this.hlsDelayCompensation}s`);
                }
                
                // Store previous phase for transition detection
                const previousPhase = this.videoPhase;
                
                // Update phase state
                this.videoPhase = newPhase;
                this.phaseStartTime = newPhaseStartTime;
                this.phaseVideoDuration = newPhaseVideoDuration;
                
                // For result phase, extract result_data and merge with response
                // The API returns result_data with match info when in result phase
                if (newPhase === 'result' && response.result_data) {
                    this.log('Found result_data in API response:', response.result_data);
                    // Merge result_data into the response for handlePhaseTransition
                    Object.assign(response, response.result_data);
                    // Store for later use when template loads
                    this.resultData = response.result_data;
                }
                
                // Handle phase transitions with delay compensation
                // Only trigger transition if phase actually changed
                if (newPhase !== previousPhase) {
                    this.log(`Video phase changed: ${previousPhase} -> ${newPhase}`);
                    this.handlePhaseTransition(previousPhase, newPhase, response);
                } else if (newPhase === 'result' && this.resultData && this.currentTemplate === 'results') {
                    // Even if phase hasn't changed, send updated result data to template
                    this.log('Sending updated result data to results template');
                    this.sendMessageToOverlay('resultUpdate', this.resultData);
                    this.sendMessageToOverlay('dataUpdated', this.resultData);
                }
                
                // Legacy support: Also check is_playing_match_video flag
                // This is now handled by the phase transition logic above
                // No need for duplicate handling here
                
                this.overlayData = response;
                
                if (hasChanges) {
                    this.sendMessageToOverlay('dataUpdated', response);
                }
            }
            
            // Also fetch timer state on each poll (only if not in match or result phase)
            if (this.videoPhase === 'idle' || this.videoPhase === 'intro') {
                const timerState = await this.fetchTimerState();
                if (timerState && (timerState.running || this.timerState.running)) {
                    // Send timer update if timer is running or was running
                    this.sendMessageToOverlay('timerUpdate', timerState);
                }
            }
            
            return response;
        } catch (error) {
            console.error('[OverlayController] Failed to fetch overlay data:', error);
            return null;
        }
    }
    
    /**
     * Handle video phase transitions with HLS delay compensation
     * @param {string} previousPhase - Previous video phase
     * @param {string} newPhase - New video phase
     * @param {Object} data - Overlay data from API
     */
    handlePhaseTransition(previousPhase, newPhase, data) {
        // Calculate delay-compensated transition time
        // We wait for HLS delay before switching templates to sync with actual video
        const delayMs = this.hlsDelayCompensation * 1000;
        
        this.log(`Scheduling phase transition from ${previousPhase} to ${newPhase} with ${delayMs}ms delay`);
        
        // Clear any pending phase transition
        if (this.phaseTransitionTimer) {
            clearTimeout(this.phaseTransitionTimer);
            this.phaseTransitionTimer = null;
        }
        
        // IMPORTANT: Stop template rotation IMMEDIATELY for match/result phases
        // Don't wait for the delay - the rotation interferes with the phase-based templates
        if (newPhase === 'match' || newPhase === 'result') {
            this.log(`Phase ${newPhase} detected - stopping template rotation immediately`);
            this.stopTemplateRotation();
        }
        
        // Schedule the template change with delay compensation
        this.phaseTransitionTimer = setTimeout(() => {
            this.executePhaseTransition(newPhase, data);
            this.phaseTransitionTimer = null;
        }, delayMs);
    }
    
    /**
     * Execute the actual phase transition (template change)
     * @param {string} phase - The new video phase
     * @param {Object} data - Overlay data from API
     */
    executePhaseTransition(phase, data) {
        this.log(`Executing phase transition to: ${phase}`);
        
        switch (phase) {
            case 'intro':
                // Intro phase - start template rotation
                this.log('Intro phase - starting template rotation');
                if (!this.rotationEnabled) {
                    this.startTemplateRotation();
                }
                break;
                
            case 'match':
                // Match phase - stop rotation and show match template
                this.log('Match phase - stopping rotation, loading match_video template');
                this.stopTemplateRotation();
                
                // Build match data with fighter names
                const matchData = {
                    match_id: data.match_id,
                    fixture_id: data.fixture_id,
                    result: data.result,
                    // Ensure match data is properly included with fighter names
                    match: data.match || (this.overlayData ? this.overlayData.match : null) || null
                };
                
                // Log the match data for debugging
                this.log('Match data for match_video template:', matchData.match);
                
                // Store match data for the template to fetch after loading
                this.matchData = matchData;
                
                // Only load if not already showing match_video
                if (this.currentTemplate !== 'match_video') {
                    this.loadTemplate('match_video');
                } else {
                    // Template already loaded, just send the data update
                    this.log('Match_video template already loaded, sending data update');
                    this.sendMessageToOverlay('matchUpdate', matchData);
                    this.sendMessageToOverlay('dataUpdated', matchData);
                }
                break;
                
            case 'result':
                // Result phase - stop rotation and show results template
                this.log('Result phase - stopping rotation, loading results template');
                this.stopTemplateRotation();
                
                // Build result data with proper match information
                // The match data should have fighter1_township and fighter2_township fields
                const resultData = {
                    outcome: data.outcome || data.result,
                    result: data.result,
                    under_over_result: data.under_over_result,
                    match_id: data.match_id,
                    fixture_id: data.fixture_id,
                    winningOutcomes: data.winningOutcomes || [],
                    // Ensure match data is properly included with fighter names
                    match: data.match || (this.overlayData ? this.overlayData.match : null) || null
                };
                
                // Log the match data for debugging
                this.log('Result data match info:', resultData.match);
                this.log('Full result data:', JSON.stringify(resultData, null, 2));
                
                // Store result data for the template to fetch after loading
                this.resultData = resultData;
                
                // Only load if not already showing results for this match
                if (this.currentTemplate !== 'results' || this.lastResultMatchId !== data.match_id) {
                    this.lastResultMatchId = data.match_id;
                    this.loadTemplate('results');
                } else {
                    // Template already loaded, just send the data update
                    this.log('Results template already loaded, sending data update');
                    this.sendMessageToOverlay('resultUpdate', resultData);
                    this.sendMessageToOverlay('dataUpdated', resultData);
                }
                break;
                
            case 'idle':
                // Idle phase - return to intro rotation after result completes
                this.log('Idle phase - will restart template rotation after delay');
                // Clear result data when returning to idle
                this.resultData = null;
                this.lastResultMatchId = null;
                // Don't immediately start rotation - wait for next intro phase
                break;
                
            default:
                this.log(`Unknown phase: ${phase}`);
        }
        
        // Emit phase change event
        this.emit('phaseChanged', { phase, data });
    }
    
    /**
     * Fetch fixture data from REST API
     * Equivalent to Qt WebChannel getFixtureData()
     */
    async fetchFixtureData() {
        try {
            this.log('Fetching fixture data from API...');
            const response = await this.apiRequest('/fixtures');
            this.log('Fixture data API response:', response);
            this.fixtureData = response;
            return response;
        } catch (error) {
            this.log('Failed to fetch fixture data:', error.message);
            return [];
        }
    }
    
    /**
     * Fetch timer state from REST API
     * Equivalent to Qt WebChannel getTimerState()
     */
    async fetchTimerState() {
        try {
            const response = await this.apiRequest('/timer');
            this.timerState = response;
            return response;
        } catch (error) {
            console.error('[OverlayController] Failed to fetch timer state:', error);
            return { running: false, remaining_seconds: 0 };
        }
    }
    
    /**
     * Fetch license text from REST API
     * Equivalent to Qt WebChannel getLicenseText()
     */
    async fetchLicenseText() {
        try {
            const response = await this.apiRequest('/license');
            this.licenseText = response.license_text || '';
            return this.licenseText;
        } catch (error) {
            console.error('[OverlayController] Failed to fetch license text:', error);
            return '';
        }
    }
    
    /**
     * Fetch completed matches from REST API
     * Equivalent to Qt WebChannel getCompletedMatches()
     */
    async fetchCompletedMatches() {
        try {
            const response = await this.apiRequest('/completed-matches');
            this.completedMatches = response;
            return response;
        } catch (error) {
            console.error('[OverlayController] Failed to fetch completed matches:', error);
            return [];
        }
    }
    
    /**
     * Fetch winning outcomes for a match
     * Equivalent to Qt WebChannel getWinningOutcomes()
     * @param {number} matchId - Match ID
     */
    async fetchWinningOutcomes(matchId) {
        try {
            const response = await this.apiRequest(`/winning-outcomes/${matchId}`);
            return response;
        } catch (error) {
            console.error('[OverlayController] Failed to fetch winning outcomes:', error);
            return [];
        }
    }
    
    /**
     * Get the current auth token from localStorage or global variable
     * @returns {string|null} - Auth token or null
     */
    getAuthToken() {
        // First check if we have a token set directly
        if (this.authToken) {
            this.log('Using directly set auth token');
            return this.authToken;
        }
        
        // Check localStorage (where app.js stores it)
        const storedToken = localStorage.getItem('authToken');
        if (storedToken) {
            this.log('Found auth token in localStorage, length:', storedToken.length);
            return storedToken;
        }
        
        // Check global authToken variable (from app.js)
        if (typeof authToken !== 'undefined' && authToken) {
            this.log('Found auth token in global variable');
            return authToken;
        }
        
        // Also check for token in sessionStorage
        const sessionToken = sessionStorage.getItem('authToken');
        if (sessionToken) {
            this.log('Found auth token in sessionStorage, length:', sessionToken.length);
            return sessionToken;
        }
        
        this.log('No auth token found in any location');
        return null;
    }
    
    /**
     * Make an authenticated API request
     * @param {string} endpoint - API endpoint (relative to apiBaseUrl)
     * @param {Object} options - Fetch options
     * @returns {Object} - Response data
     */
    async apiRequest(endpoint, options = {}) {
        const url = `${this.apiBaseUrl}${endpoint}`;
        
        const headers = {
            'Content-Type': 'application/json',
            ...options.headers
        };
        
        // Get token from multiple sources
        const token = this.getAuthToken();
        if (token) {
            headers['Authorization'] = `Bearer ${token}`;
            this.log('API request to:', endpoint, 'with auth token');
        }
        
        this.log('Making API request to:', url);
        
        try {
            const response = await fetch(url, {
                ...options,
                headers
            });
            
            this.log('API response status:', response.status, response.statusText);
            
            if (!response.ok) {
                const errorText = await response.text();
                this.log('API error response:', errorText);
                throw new Error(`API request failed: ${response.status} ${response.statusText}`);
            }
            
            const data = await response.json();
            this.log('API response data:', data);
            return data;
        } catch (error) {
            this.log('API request error:', error.message);
            throw error;
        }
    }
    
    /**
     * Send a message to the overlay iframe
     * @param {string} type - Message type
     * @param {Object} data - Message data
     */
    sendMessageToOverlay(type, data) {
        if (this.overlayIframe && this.overlayIframe.contentWindow) {
            const message = { type, ...data };
            this.log('Sending message to overlay:', type, data);
            this.overlayIframe.contentWindow.postMessage(message, '*');
        } else {
            this.log('Cannot send message - iframe not ready');
        }
    }
    
    /**
     * Handle messages from the overlay iframe
     * @param {MessageEvent} event - Message event
     */
    async onIframeMessage(event) {
        const message = event.data;
        
        // Skip if not an object or no type
        if (!message || typeof message !== 'object' || !message.type) {
            return;
        }
        
        this.log('Received iframe message:', message.type, 'from origin:', event.origin);
        
        // Handle different message types from iframe
        switch (message.type) {
            case 'requestInitialData':
                // Send current overlay data to iframe
                this.log('Processing requestInitialData');
                this.sendMessageToOverlay('initialData', {
                    overlayData: this.overlayData,
                    timerState: this.timerState,
                    licenseText: this.licenseText
                });
                // Also send result data if this is the results template
                if (this.resultData) {
                    this.log('Sending result data with initialData:', this.resultData);
                    this.sendMessageToOverlay('resultUpdate', this.resultData);
                }
                // Also send match data if this is the match_video template
                if (this.matchData) {
                    this.log('Sending match data with initialData:', this.matchData);
                    this.sendMessageToOverlay('matchUpdate', this.matchData);
                }
                break;
                
            case 'requestFixtureData':
                this.log('Processing requestFixtureData');
                try {
                    const fixtureData = await this.fetchFixtureData();
                    this.log('Fetched fixture data:', fixtureData ? fixtureData.length + ' fixtures' : 'null');
                    this.sendMessageToOverlay('fixtureData', { fixtures: fixtureData });
                } catch (error) {
                    this.log('Error fetching fixture data:', error.message);
                    this.sendMessageToOverlay('fixtureData', { fixtures: [] });
                }
                break;
                
            case 'requestTimerState':
                const timerState = await this.fetchTimerState();
                this.sendMessageToOverlay('timerState', timerState);
                break;
                
            case 'requestLicenseText':
                const licenseText = await this.fetchLicenseText();
                this.sendMessageToOverlay('licenseText', { licenseText });
                break;
                
            case 'requestCompletedMatches':
                const completedMatches = await this.fetchCompletedMatches();
                this.sendMessageToOverlay('completedMatches', { matches: completedMatches });
                break;
                
            case 'requestWinningOutcomes':
                const outcomes = await this.fetchWinningOutcomes(message.matchId);
                this.sendMessageToOverlay('winningOutcomes', { outcomes });
                break;
                
            case 'log':
                // Forward log messages from iframe to console (only in debug mode)
                this.log('[Overlay]', message.message);
                break;
                
            default:
                this.log('Unknown iframe message type:', message.type);
        }
    }
    
    /**
     * Handle video timeupdate event
     * Synchronizes overlay with video playback position
     */
    onTimeUpdate() {
        const now = Date.now();
        
        // Throttle position updates
        if (now - this.lastPositionUpdate < this.positionUpdateInterval) {
            return;
        }
        
        this.lastPositionUpdate = now;
        
        const position = this.videoElement.currentTime;
        const duration = this.videoElement.duration || 0;
        
        // Send position update to overlay
        this.sendMessageToOverlay('positionChanged', {
            position: position,
            duration: duration
        });
        
        // Check for template triggers
        this.checkTemplateTriggers(position);
    }
    
    /**
     * Handle video play event
     */
    onPlay() {
        this.log('Video playing');
        this.sendMessageToOverlay('videoStateChanged', {
            state: 'playing',
            position: this.videoElement.currentTime,
            duration: this.videoElement.duration
        });
    }
    
    /**
     * Handle video pause event
     */
    onPause() {
        this.log('Video paused');
        this.sendMessageToOverlay('videoStateChanged', {
            state: 'paused',
            position: this.videoElement.currentTime,
            duration: this.videoElement.duration
        });
    }
    
    /**
     * Handle video ended event
     */
    onEnded() {
        this.log('Video ended');
        this.sendMessageToOverlay('videoStateChanged', {
            state: 'ended',
            position: this.videoElement.currentTime,
            duration: this.videoElement.duration
        });
    }
    
    /**
     * Handle video loadedmetadata event
     */
    onLoadedMetadata() {
        this.log('Video metadata loaded');
        this.sendMessageToOverlay('videoLoaded', {
            duration: this.videoElement.duration,
            videoWidth: this.videoElement.videoWidth,
            videoHeight: this.videoElement.videoHeight
        });
    }
    
    /**
     * Check for time-based template triggers
     * @param {number} position - Current video position in seconds
     */
    checkTemplateTriggers(position) {
        for (const trigger of this.templateTriggers) {
            if (position >= trigger.time && !trigger.fired) {
                trigger.fired = true;
                this.loadTemplate(trigger.template);
            }
        }
    }
    
    /**
     * Add a template trigger
     * @param {number} time - Time in seconds to trigger
     * @param {string} template - Template name to load
     */
    addTemplateTrigger(time, template) {
        this.templateTriggers.push({
            time: time,
            template: template,
            fired: false
        });
    }
    
    /**
     * Clear all template triggers
     */
    clearTemplateTriggers() {
        this.templateTriggers = [];
    }
    
    /**
     * Reset template triggers (for video loop/restart)
     */
    resetTemplateTriggers() {
        for (const trigger of this.templateTriggers) {
            trigger.fired = false;
        }
    }
    
    /**
     * Update overlay data directly
     * @param {Object} data - Overlay data to update
     */
    updateOverlayData(data) {
        this.overlayData = { ...this.overlayData, ...data };
        this.sendMessageToOverlay('dataUpdated', data);
    }
    
    /**
     * Show the overlay container
     */
    show() {
        if (this.overlayContainer) {
            this.overlayContainer.style.display = 'block';
        }
    }
    
    /**
     * Hide the overlay container
     */
    hide() {
        if (this.overlayContainer) {
            this.overlayContainer.style.display = 'none';
        }
    }
    
    /**
     * Toggle overlay visibility
     */
    toggle() {
        if (this.overlayContainer) {
            if (this.overlayContainer.style.display === 'none') {
                this.show();
            } else {
                this.hide();
            }
        }
    }
    
    /**
     * Set up resize observer to update overlay scale when video container resizes
     */
    setupResizeObserver() {
        if (!this.overlayContainer) {
            return;
        }
        
        // Use ResizeObserver if available
        if (typeof ResizeObserver !== 'undefined') {
            this.resizeObserver = new ResizeObserver(() => {
                this.updateOverlayScale();
            });
            this.resizeObserver.observe(this.overlayContainer.parentElement || this.overlayContainer);
            this.log('ResizeObserver set up for overlay scaling');
        } else {
            // Fallback to window resize event
            window.addEventListener('resize', () => this.updateOverlayScale());
            this.log('Using window resize event for overlay scaling');
        }
    }
    
    /**
     * Update overlay scale to fit the video container
     * Templates are designed for 1920x1080, so we scale down to fit
     */
    updateOverlayScale() {
        if (!this.overlayContainer || !this.overlayIframe) {
            return;
        }
        
        // Get the container dimensions
        const containerRect = this.overlayContainer.getBoundingClientRect();
        const containerWidth = containerRect.width;
        const containerHeight = containerRect.height;
        
        // Calculate scale to fit container while maintaining aspect ratio
        // Templates are designed for 1920x1080 (16:9 aspect ratio)
        const scaleX = containerWidth / this.templateBaseWidth;
        const scaleY = containerHeight / this.templateBaseHeight;
        
        // Use the smaller scale to ensure the entire overlay fits
        this.currentScale = Math.min(scaleX, scaleY);
        
        // Apply scale transform to iframe
        this.overlayIframe.style.transform = `scale(${this.currentScale})`;
        
        // Adjust container height to match scaled content
        // This prevents extra space at the bottom
        const scaledHeight = this.templateBaseHeight * this.currentScale;
        const scaledWidth = this.templateBaseWidth * this.currentScale;
        
        // Center the overlay if it doesn't fill the entire container
        const offsetX = (containerWidth - scaledWidth) / 2;
        const offsetY = (containerHeight - scaledHeight) / 2;
        
        this.overlayIframe.style.position = 'absolute';
        this.overlayIframe.style.left = `${offsetX}px`;
        this.overlayIframe.style.top = `${offsetY}px`;
        
        this.log(`Overlay scaled to ${this.currentScale.toFixed(3)} (${containerWidth}x${containerHeight} -> ${scaledWidth.toFixed(0)}x${scaledHeight.toFixed(0)})`);
        
        // Emit scale change event
        this.emit('scaleChanged', {
            scale: this.currentScale,
            containerWidth,
            containerHeight,
            scaledWidth,
            scaledHeight
        });
    }
    
    /**
     * Get the current overlay scale
     * @returns {number} - Current scale factor
     */
    getScale() {
        return this.currentScale;
    }
    
    /**
     * Pause overlay updates
     */
    pause() {
        this.isPaused = true;
        this.stopPolling();
        this.log('Overlay paused');
    }
    
    /**
     * Resume overlay updates
     */
    resume() {
        this.isPaused = false;
        this.startPolling();
        this.log('Overlay resumed');
    }
    
    /**
     * Handle stream start event (called from player.js)
     */
    onStreamStart() {
        this.log('Stream started');
        this.resume();
        this.sendMessageToOverlay('streamStateChanged', { state: 'live' });
    }
    
    /**
     * Handle stream stop event (called from player.js)
     */
    onStreamStop() {
        this.log('Stream stopped');
        this.sendMessageToOverlay('streamStateChanged', { state: 'offline' });
    }
    
    /**
     * Handle stream pause event (called from player.js)
     */
    onStreamPause() {
        this.log('Stream paused');
        this.sendMessageToOverlay('streamStateChanged', { state: 'paused' });
    }
    
    /**
     * Handle video time update (called from player.js)
     * @param {number} currentTime - Current video time in seconds
     * @param {number} duration - Video duration in seconds
     */
    onVideoTimeUpdate(currentTime, duration) {
        if (this.isPaused) return;
        
        const now = Date.now();
        
        // Throttle position updates
        if (now - this.lastPositionUpdate < this.positionUpdateInterval) {
            return;
        }
        
        this.lastPositionUpdate = now;
        
        // Send position update to overlay
        this.sendMessageToOverlay('positionChanged', {
            position: currentTime,
            duration: duration
        });
        
        // Check for template triggers
        this.checkTemplateTriggers(currentTime);
    }
    
    /**
     * Handle video seek event (called from player.js)
     * @param {number} currentTime - Current video time in seconds
     */
    onVideoSeek(currentTime) {
        this.log('Video seeked to:', currentTime);
        
        // Reset template triggers for seek
        this.resetTemplateTriggers();
        
        // Send seek event to overlay
        this.sendMessageToOverlay('videoSeeked', {
            position: currentTime
        });
    }
    
    /**
     * Cleanup and destroy the controller
     */
    destroy() {
        this.log('Destroying overlay controller');
        
        // Stop template rotation
        this.stopTemplateRotation();
        
        // Stop polling
        this.stopPolling();
        
        // Clear phase transition timer
        if (this.phaseTransitionTimer) {
            clearTimeout(this.phaseTransitionTimer);
            this.phaseTransitionTimer = null;
        }
        
        // Close WebSocket
        if (this.websocket) {
            this.websocket.close();
            this.websocket = null;
        }
        
        // Disconnect resize observer
        if (this.resizeObserver) {
            this.resizeObserver.disconnect();
            this.resizeObserver = null;
        }
        
        // Remove event listeners
        if (this.videoElement) {
            this.videoElement.removeEventListener('timeupdate', this.onTimeUpdate);
            this.videoElement.removeEventListener('play', this.onPlay);
            this.videoElement.removeEventListener('pause', this.onPause);
            this.videoElement.removeEventListener('ended', this.onEnded);
            this.videoElement.removeEventListener('loadedmetadata', this.onLoadedMetadata);
        }
        
        window.removeEventListener('message', this.onIframeMessage);
        window.removeEventListener('resize', this.updateOverlayScale);
        
        this.initialized = false;
    }
}

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
    module.exports = WebOverlayController;
}