class OverlayManager {
    constructor() {
        this.overlayData = {};
        this.pendingUpdates = [];
        this.webChannelReady = false;
        this.initWebChannel();
        this.initCanvas();
    }

    initWebChannel() {
        try {
            // Wait for DOM to be fully loaded
            this.waitForFullInitialization(() => {
                // Check if Qt WebChannel transport is available (set by QWebEnginePage.setWebChannel)
                if (typeof qt !== 'undefined' && qt.webChannelTransport) {
                    // Use the transport provided by Qt WebEngine
                    new QWebChannel(qt.webChannelTransport, (channel) => {
                        // Connect to overlay object
                        window.overlay = channel.objects.overlay;
    
                        // Flush any buffered console messages
                        if (window.flushConsoleBuffer) {
                            window.flushConsoleBuffer();
                        }
    
                        // Connect signals if overlay object exists
                        if (window.overlay) {
                            // Connect positionChanged signal
                            if (window.overlay.positionChanged) {
                                window.overlay.positionChanged.connect((position, duration) => {
                                    if (position !== null && duration !== null) {
                                        this.updateProgress(position, duration);
                                    } else {
                                        console.warn('positionChanged signal received null/undefined parameters, skipping');
                                    }
                                });
                            }
    
                            // Connect videoInfoChanged signal
                            if (window.overlay.videoInfoChanged) {
                                window.overlay.videoInfoChanged.connect((info) => {
                                    if (info && typeof info === 'object') {
                                        this.updateVideoInfo(info);
                                    } else {
                                        console.warn('videoInfoChanged signal received null/undefined parameter, skipping');
                                    }
                                });
                            }
    
                            // Process pending updates after full initialization
                            setTimeout(() => this.processPendingUpdates(), 100);
                            console.log('WebChannel connected and ready');
                        } else {
                            console.warn('Overlay object not found in WebChannel');
                        }
                    });
                } else {
                    console.warn('Qt WebChannel transport not available, falling back to QtWebChannel constructor');
                    // Fallback: try the old method
                    if (typeof Qt === 'object' && typeof QtWebChannel === 'function') {
                        const channel = new QtWebChannel();
                        channel.connectTo('overlay', (overlay) => {
                            window.overlay = overlay;
    
                            // Connect positionChanged signal
                            overlay.positionChanged.connect((position, duration) => {
                                if (position !== null && duration !== null) {
                                    this.updateProgress(position, duration);
                                } else {
                                    console.warn('positionChanged signal received null/undefined parameters, skipping');
                                }
                            });
    
                            // Connect videoInfoChanged signal
                            overlay.videoInfoChanged.connect((info) => {
                                if (info && typeof info === 'object') {
                                    this.updateVideoInfo(info);
                                } else {
                                    console.warn('videoInfoChanged signal received null/undefined parameter, skipping');
                                }
                            });
    
                            // Process pending updates after full initialization
                            setTimeout(() => this.processPendingUpdates(), 100);
                            console.log('WebChannel connected via fallback method');
                        });
                    } else {
                        console.warn('QtWebChannel not available either');
                        // Retry with exponential backoff
                        setTimeout(() => this.initWebChannel(), 1000);
                    }
                }
            });
        } catch (error) {
            console.error('WebChannel initialization error:', error);
            // Retry with exponential backoff
            setTimeout(() => this.initWebChannel(), 1000);
        }
    }

    waitForFullInitialization(callback) {
        const checkReady = () => {
            if (document.readyState === 'complete' && this.validateCriticalElements()) {
                callback();
            } else {
                setTimeout(checkReady, 100);
            }
        };
        checkReady();
    }

    processPendingUpdates() {
        // Prevent infinite loops by limiting processing attempts
        let processed = 0;
        const maxProcessing = 10;
        
        while (this.pendingUpdates.length > 0 && processed < maxProcessing) {
            // Double-check readiness before processing pending update
            if (!this.isSystemReady()) {
                console.log('System not ready during pending updates processing');
                break;
            }
            
            const update = this.pendingUpdates.shift();
            // Add null check before processing pending update
            if (update && typeof update === 'object') {
                this.updateOverlay(update);
                processed++;
            } else {
                console.warn('Skipping null/invalid pending update:', update);
                processed++;
            }
        }
        
        // If there are still pending updates, schedule another processing cycle
        if (this.pendingUpdates.length > 0) {
            setTimeout(() => this.processPendingUpdates(), 300);
        }
    }

    updateOverlay(data) {
        // Comprehensive null/undefined check for data parameter
        if (!data) {
            console.warn('updateOverlay called with null/undefined data');
            console.warn('Call stack:', new Error().stack);
            return;
        }
        
        console.log('Updating overlay with data:', data);
        
        // Enhanced readiness check with multiple validation layers
        if (!this.isSystemReady()) {
            console.log('System not ready, queuing update');
            this.pendingUpdates.push(data);
            // Retry with progressive backoff
            setTimeout(() => this.processPendingUpdates(), 150);
            return;
        }
        
        // Validate all critical elements exist before any updates
        if (!this.validateCriticalElements()) {
            console.warn('Critical elements not available, requeueing update');
            this.pendingUpdates.push(data);
            setTimeout(() => this.processPendingUpdates(), 200);
            return;
        }
        
        this.overlayData = { ...this.overlayData, ...data };
        
        // Update title elements with safe element access
        // Additional null check for data.title
        if (data.hasOwnProperty('title') && data.title !== undefined && data.title !== null) {
            if (!this.safeUpdateElement('titleMain', data.title, 'textContent')) {
                console.warn('Failed to update titleMain, queuing for retry');
                this.pendingUpdates.push({title: data.title});
                return;
            }
        }
        
        // Additional null check for data.subtitle
        if (data.hasOwnProperty('subtitle') && data.subtitle !== undefined && data.subtitle !== null) {
            if (!this.safeUpdateElement('titleSubtitle', data.subtitle, 'textContent')) {
                console.warn('Failed to update titleSubtitle, queuing for retry');
                this.pendingUpdates.push({subtitle: data.subtitle});
                return;
            }
        }
        
        // Update ticker text with null check
        if (data.hasOwnProperty('ticker') && data.ticker !== undefined && data.ticker !== null) {
            if (!this.safeUpdateElement('tickerText', data.ticker, 'textContent')) {
                console.warn('Failed to update tickerText, queuing for retry');
                this.pendingUpdates.push({ticker: data.ticker});
                return;
            }
        }
        
        // Show/hide stats panel with null check
        if (data.hasOwnProperty('showStats') && data.showStats !== undefined && data.showStats !== null) {
            if (!this.safeUpdateElement('statsPanel', data.showStats ? 'block' : 'none', 'display')) {
                console.warn('Failed to update statsPanel, queuing for retry');
                this.pendingUpdates.push({showStats: data.showStats});
                return;
            }
        }
        
        // Update custom CSS if provided with null check
        if (data.hasOwnProperty('customCSS') && data.customCSS !== undefined && data.customCSS !== null) {
            this.applyCustomCSS(data.customCSS);
        }
    }

    isSystemReady() {
        try {
            return this.webChannelReady &&
                   document.readyState === 'complete' &&
                   document.getElementById('titleMain') !== null &&
                   document.body !== null;
        } catch (error) {
            console.warn('Error in isSystemReady check:', error);
            return false;
        }
    }

    validateCriticalElements() {
        try {
            const criticalIds = ['titleMain', 'titleSubtitle', 'tickerText', 'statsPanel', 'progressBar'];
            
            for (const id of criticalIds) {
                const element = document.getElementById(id);
                if (!element) {
                    console.warn(`Critical element ${id} not found`);
                    return false;
                }
                // Additional check for element validity
                if (element.parentNode === null || !document.contains(element)) {
                    console.warn(`Critical element ${id} not properly attached to DOM`);
                    return false;
                }
            }
            return true;
        } catch (error) {
            console.warn('Error in validateCriticalElements:', error);
            return false;
        }
    }

    safeUpdateElement(elementId, value, property = 'textContent') {
        // Null check for elementId parameter
        if (elementId === null || elementId === undefined) {
            console.warn('safeUpdateElement called with null/undefined elementId');
            return false;
        }
        
        // For textContent property, convert null/undefined to empty string
        if (property === 'textContent' && (value === null || value === undefined)) {
            value = '';
        }
        
        // For other properties, null/undefined values are not allowed
        if (property !== 'textContent' && (value === null || value === undefined)) {
            console.warn(`safeUpdateElement called with null/undefined value for property ${property}`);
            return false;
        }
        
        try {
            const element = document.getElementById(elementId);
            if (!element) {
                console.warn(`Element ${elementId} not found`);
                return false;
            }
            
            // Double-check element is still valid and in DOM
            if (element.parentNode === null) {
                console.warn(`Element ${elementId} no longer in DOM`);
                return false;
            }
            
            // Additional check for element accessibility
            if (!document.contains(element)) {
                console.warn(`Element ${elementId} not contained in document`);
                return false;
            }
            
            if (property === 'display') {
                element.style.display = value;
            } else if (property === 'width') {
                element.style.width = value;
            } else if (property === 'textContent') {
                // Check if textContent property exists and is writable
                if ('textContent' in element) {
                    element.textContent = value;
                    // Animate only if element update succeeded
                    this.animateElement(elementId, 'pulse');
                } else {
                    console.warn(`Element ${elementId} does not support textContent`);
                    return false;
                }
            }
            
            return true;
        } catch (error) {
            console.error(`Error updating element ${elementId}:`, error);
            return false;
        }
    }

    updateProgress(position, duration) {
        // Null checks for position and duration parameters
        if (position === null || position === undefined) {
            console.warn('updateProgress called with null/undefined position');
            return;
        }
        
        if (duration === null || duration === undefined) {
            console.warn('updateProgress called with null/undefined duration');
            return;
        }
        
        try {
            // Check system readiness before updating progress
            if (!this.isSystemReady()) {
                console.log('System not ready for progress update, skipping');
                return;
            }
            
            const percentage = duration > 0 ? (position / duration) * 100 : 0;
            
            // Safe progress bar update
            this.safeUpdateElement('progressBar', `${percentage}%`, 'width');
            
        } catch (error) {
            console.error('Error updating progress:', error);
        }
    }

    updateVideoInfo(info) {
        // Comprehensive null/undefined check for info parameter
        if (!info) {
            console.warn('updateVideoInfo called with null/undefined info');
            return;
        }
        
        console.log('Video info updated:', info);
        
        if (info.hasOwnProperty('resolution') && info.resolution !== undefined && info.resolution !== null) {
            const resolutionElement = document.getElementById('resolution');
            if (resolutionElement) {
                resolutionElement.textContent = info.resolution;
            }
        }
        if (info.hasOwnProperty('bitrate') && info.bitrate !== undefined && info.bitrate !== null) {
            const bitrateElement = document.getElementById('bitrate');
            if (bitrateElement) {
                bitrateElement.textContent = info.bitrate;
            }
        }
        if (info.hasOwnProperty('codec') && info.codec !== undefined && info.codec !== null) {
            const codecElement = document.getElementById('codec');
            if (codecElement) {
                codecElement.textContent = info.codec;
            }
        }
        if (info.hasOwnProperty('fps') && info.fps !== undefined && info.fps !== null) {
            const fpsElement = document.getElementById('fps');
            if (fpsElement) {
                fpsElement.textContent = info.fps;
            }
        }
    }

    formatTime(seconds) {
        const mins = Math.floor(seconds / 60);
        const secs = Math.floor(seconds % 60);
        return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
    }

    animateElement(elementId, animationClass) {
        // Null checks for elementId and animationClass parameters
        if (elementId === null || elementId === undefined) {
            console.warn('animateElement called with null/undefined elementId');
            return;
        }
        
        if (animationClass === null || animationClass === undefined) {
            console.warn('animateElement called with null/undefined animationClass');
            return;
        }
        
        const element = document.getElementById(elementId);
        if (element) {
            element.style.animation = 'none';
            element.offsetHeight; // Trigger reflow
            element.style.animation = `${animationClass} 1s ease-in-out`;
        }
    }

    applyCustomCSS(css) {
        let styleElement = document.getElementById('customStyles');
        if (!styleElement) {
            styleElement = document.createElement('style');
            styleElement.id = 'customStyles';
            document.head.appendChild(styleElement);
        }
        styleElement.textContent = css;
    }

    initCanvas() {
        this.canvas = document.getElementById('canvasOverlay');
        this.ctx = this.canvas ? this.canvas.getContext('2d') : null;
        this.animationFrame = null;
        
        // Initialize canvas dimensions
        this.resizeCanvas();
        
        // Setup resize listener
        window.addEventListener('resize', () => this.resizeCanvas());
    }

    resizeCanvas() {
        if (!this.canvas) return;
        
        // Get window dimensions
        const width = window.innerWidth;
        const height = window.innerHeight;
        
        // Update canvas dimensions
        this.canvas.width = width;
        this.canvas.height = height;
        
        // Redraw if animations are running
        if (this.animationFrame) {
            this.startCanvasAnimations();
        }
    }

    startCanvasAnimations() {
        if (!this.canvas || !this.ctx) {
            console.warn('Canvas not ready for animations');
            return;
        }
        
        const animate = () => {
            if (this.ctx && this.canvas) {
                this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
                
                // Draw animated particles or custom graphics
                this.drawParticles();
                
                this.animationFrame = requestAnimationFrame(animate);
            }
        };
        animate();
    }

    drawParticles() {
        // Example particle system - can be customized
        const time = Date.now() * 0.001;
        
        for (let i = 0; i < 5; i++) {
            const x = (Math.sin(time + i) * 100) + this.canvas.width / 2;
            const y = (Math.cos(time + i * 0.5) * 50) + this.canvas.height / 2;
            
            this.ctx.beginPath();
            this.ctx.arc(x, y, 2, 0, Math.PI * 2);
            this.ctx.fillStyle = `rgba(255, 255, 255, ${0.1 + Math.sin(time + i) * 0.1})`;
            this.ctx.fill();
        }
    }

    // Public API for Python to call
    setTitle(title) {
        // Null check for title parameter
        if (title === null || title === undefined) {
            console.warn('setTitle called with null/undefined title');
            return;
        }
        this.updateOverlay({ title });
    }

    setSubtitle(subtitle) {
        // Null check for subtitle parameter
        if (subtitle === null || subtitle === undefined) {
            console.warn('setSubtitle called with null/undefined subtitle');
            return;
        }
        this.updateOverlay({ subtitle });
    }

    setTicker(ticker) {
        // Null check for ticker parameter
        if (ticker === null || ticker === undefined) {
            console.warn('setTicker called with null/undefined ticker');
            return;
        }
        this.updateOverlay({ ticker });
    }

    showStats(show) {
        // Null check for show parameter
        if (show === null || show === undefined) {
            console.warn('showStats called with null/undefined show');
            return;
        }
        this.updateOverlay({ showStats: show });
    }

    cleanup() {
        if (this.animationFrame) {
            cancelAnimationFrame(this.animationFrame);
        }
    }
}

// Initialize overlay manager immediately and safely
let overlayManager = null;

// Function to ensure DOM is ready before any operations
function ensureOverlayReady(callback) {
    if (overlayManager && overlayManager.webChannelReady) {
        callback();
    } else {
        setTimeout(() => ensureOverlayReady(callback), 50);
    }
}

// Initialize when DOM is ready
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
        overlayManager = new OverlayManager();
        window.overlayManager = overlayManager;
    });
} else {
    // DOM already loaded
    overlayManager = new OverlayManager();
    window.overlayManager = overlayManager;
}

// Cleanup on unload
window.addEventListener('beforeunload', () => {
    if (overlayManager) {
        overlayManager.cleanup();
    }
});

// Safe global functions that wait for overlay to be ready
window.safeUpdateOverlay = function(data) {
    // Comprehensive null/undefined check for data parameter
    if (!data) {
        console.warn('safeUpdateOverlay called with null/undefined data');
        return;
    }
    
    ensureOverlayReady(() => {
        if (overlayManager) {
            overlayManager.updateOverlay(data);
        }
    });
};

window.safeUpdateProgress = function(position, duration) {
    // Null checks for position and duration parameters
    if (position === null || position === undefined) {
        console.warn('safeUpdateProgress called with null/undefined position');
        return;
    }
    
    if (duration === null || duration === undefined) {
        console.warn('safeUpdateProgress called with null/undefined duration');
        return;
    }
    
    ensureOverlayReady(() => {
        if (overlayManager) {
            overlayManager.updateProgress(position, duration);
        }
    });
};