/**
 * Theme Color Detector
 * Detects and extracts theme colors from web pages for titlebar coloring.
 * Mimics Safari's web app titlebar color matching feature.
 */

(function() {
    'use strict';
    
    const DETECTION_DEBOUNCE_MS = 100;
    let lastDetectedColor = null;
    let detectionTimeout = null;
    let mutationObserver = null;
    
    /**
     * Parse a color string and return RGBA components
     * @param {string} colorStr - CSS color string
     * @returns {{r: number, g: number, b: number, a: number}|null}
     */
    function parseColor(colorStr) {
        if (!colorStr || colorStr === 'transparent' || colorStr === 'initial' || colorStr === 'inherit') {
            return null;
        }
        
        const canvas = document.createElement('canvas');
        canvas.width = canvas.height = 1;
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, 1, 1);
        ctx.fillStyle = colorStr;
        ctx.fillRect(0, 0, 1, 1);
        const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
        
        return { r, g, b, a: a / 255 };
    }
    
    /**
     * Convert RGBA to hex color string
     * @param {{r: number, g: number, b: number, a: number}} color
     * @returns {string}
     */
    function colorToHex(color) {
        if (!color) return null;
        const toHex = (n) => n.toString(16).padStart(2, '0');
        return `#${toHex(color.r)}${toHex(color.g)}${toHex(color.b)}`;
    }
    
    /**
     * Blend a color with alpha over a background color
     * @param {{r: number, g: number, b: number, a: number}} fg - Foreground color
     * @param {{r: number, g: number, b: number, a: number}} bg - Background color
     * @returns {{r: number, g: number, b: number, a: number}}
     */
    function blendColors(fg, bg) {
        if (!fg) return bg;
        if (!bg || fg.a >= 1) return { ...fg, a: 1 };
        
        const alpha = fg.a;
        return {
            r: Math.round(fg.r * alpha + bg.r * (1 - alpha)),
            g: Math.round(fg.g * alpha + bg.g * (1 - alpha)),
            b: Math.round(fg.b * alpha + bg.b * (1 - alpha)),
            a: 1
        };
    }
    
    /**
     * Calculate relative luminance for contrast ratio
     * @param {{r: number, g: number, b: number}} color
     * @returns {number}
     */
    function getLuminance(color) {
        const [rs, gs, bs] = [color.r, color.g, color.b].map(c => {
            c = c / 255;
            return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
        });
        return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
    }
    
    /**
     * Determine if a color is light or dark
     * @param {{r: number, g: number, b: number}} color
     * @returns {boolean} true if light, false if dark
     */
    function isLightColor(color) {
        return getLuminance(color) > 0.5;
    }
    
    /**
     * Get the current color scheme preference
     * @returns {'light'|'dark'}
     */
    function getColorScheme() {
        return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
    }
    
    /**
     * Check theme-color meta tags with media query support
     * @returns {string|null}
     */
    function getMetaThemeColor() {
        const scheme = getColorScheme();
        
        // First, try to find a theme-color with matching media query
        const metaTags = document.querySelectorAll('meta[name="theme-color"]');
        let genericColor = null;
        
        for (const meta of metaTags) {
            const media = meta.getAttribute('media');
            const content = meta.getAttribute('content');
            
            if (!content) continue;
            
            if (media) {
                // Check for prefers-color-scheme media query
                if (media.includes(`prefers-color-scheme: ${scheme}`) || 
                    media.includes(`prefers-color-scheme:${scheme}`)) {
                    const parsed = parseColor(content);
                    if (parsed && parsed.a >= 0.9) {
                        return colorToHex(parsed);
                    }
                }
            } else {
                // Store generic (no media query) as fallback
                const parsed = parseColor(content);
                if (parsed && parsed.a >= 0.9) {
                    genericColor = colorToHex(parsed);
                }
            }
        }
        
        return genericColor;
    }
    
    /**
     * Check for a fixed/sticky header element at the top
     * @returns {string|null}
     */
    function getFixedHeaderColor() {
        const viewportWidth = window.innerWidth;
        const centerX = viewportWidth / 2;
        const topY = 5; // Check near the top
        
        const elements = document.elementsFromPoint(centerX, topY);
        
        for (const el of elements) {
            if (el === document.documentElement || el === document.body) continue;
            
            const style = getComputedStyle(el);
            const position = style.position;
            
            // Check if element is fixed or sticky
            if (position !== 'fixed' && position !== 'sticky') continue;
            
            const rect = el.getBoundingClientRect();
            
            // Check if element is positioned at top (allow some tolerance)
            if (rect.top > 10) continue;
            
            // Check if element spans at least 85% of viewport width
            if (rect.width < viewportWidth * 0.85) continue;
            
            // Get the background color
            const bgColor = parseColor(style.backgroundColor);
            
            if (!bgColor) continue;
            
            // Check opacity requirements
            if (bgColor.a < 0.8) continue;
            
            if (bgColor.a >= 1) {
                return colorToHex(bgColor);
            }
            
            // If alpha is between 0.8 and 1, try to blend with document background
            if (bgColor.a >= 0.9) {
                // Try to get document background
                const docBg = getDocumentBackgroundColor();
                if (docBg) {
                    const blended = blendColors(bgColor, docBg);
                    return colorToHex(blended);
                }
                // If no doc background but alpha >= 0.9, use it anyway
                return colorToHex({ ...bgColor, a: 1 });
            }
            
            // Alpha is between 0.8 and 0.9, try to blend
            const docBg = getDocumentBackgroundColor();
            if (docBg) {
                const blended = blendColors(bgColor, docBg);
                return colorToHex(blended);
            }
            
            // Can't use this color (alpha < 0.9 and no background to blend)
        }
        
        return null;
    }
    
    /**
     * Fetch and check the web app manifest for theme-color
     * @returns {Promise<string|null>}
     */
    async function getManifestThemeColor() {
        try {
            const manifestLink = document.querySelector('link[rel="manifest"]');
            if (!manifestLink) return null;
            
            const manifestUrl = manifestLink.href;
            const response = await fetch(manifestUrl);
            if (!response.ok) return null;
            
            const manifest = await response.json();
            if (manifest.theme_color) {
                const parsed = parseColor(manifest.theme_color);
                if (parsed && parsed.a >= 0.9) {
                    return colorToHex(parsed);
                }
            }
        } catch (e) {
            // Manifest fetch failed, ignore
        }
        return null;
    }
    
    /**
     * Get the document background color from body or html
     * @returns {{r: number, g: number, b: number, a: number}|null}
     */
    function getDocumentBackgroundColor() {
        // Try body first
        const bodyStyle = getComputedStyle(document.body);
        let bgColor = parseColor(bodyStyle.backgroundColor);
        
        if (bgColor && bgColor.a >= 0.9) {
            return bgColor;
        }
        
        // Try html element
        const htmlStyle = getComputedStyle(document.documentElement);
        bgColor = parseColor(htmlStyle.backgroundColor);
        
        if (bgColor && bgColor.a >= 0.9) {
            return bgColor;
        }
        
        return null;
    }
    
    /**
     * Get background color as fallback (from body/html)
     * @returns {string|null}
     */
    function getDocumentBackgroundAsTheme() {
        const bgColor = getDocumentBackgroundColor();
        if (bgColor) {
            return colorToHex(bgColor);
        }
        return null;
    }
    
    /**
     * Main detection function - tries all methods in order
     * @returns {Promise<{color: string|null, isLight: boolean}>}
     */
    async function detectThemeColor() {
        let color = null;
        
        // 1. Check meta theme-color tags (with media query support)
        color = getMetaThemeColor();
        if (color) {
            const parsed = parseColor(color);
            return { color, isLight: isLightColor(parsed) };
        }
        
        // 2. Check for fixed/sticky header element
        color = getFixedHeaderColor();
        if (color) {
            const parsed = parseColor(color);
            return { color, isLight: isLightColor(parsed) };
        }
        
        // 3. Check manifest theme-color
        color = await getManifestThemeColor();
        if (color) {
            const parsed = parseColor(color);
            return { color, isLight: isLightColor(parsed) };
        }
        
        // 4. Check document background color
        color = getDocumentBackgroundAsTheme();
        if (color) {
            const parsed = parseColor(color);
            return { color, isLight: isLightColor(parsed) };
        }
        
        // 5. No theme color found, use default
        return { color: null, isLight: null };
    }
    
    /**
     * Send the detected color to the main process
     * @param {{color: string|null, isLight: boolean}} themeData
     */
    function sendThemeColor(themeData) {
        if (window.__swai_theme_color_callback) {
            window.__swai_theme_color_callback(themeData);
        }
    }
    
    /**
     * Schedule a theme color detection using requestIdleCallback
     */
    function scheduleDetection() {
        if (detectionTimeout) {
            cancelIdleCallback(detectionTimeout);
        }
        
        detectionTimeout = requestIdleCallback(async () => {
            const themeData = await detectThemeColor();
            
            // Only send if color changed
            const colorString = JSON.stringify(themeData);
            if (colorString !== lastDetectedColor) {
                lastDetectedColor = colorString;
                sendThemeColor(themeData);
            }
        }, { timeout: 500 }); // Max delay of 500ms
    }
    
    /**
     * Debounced detection for frequent events
     */
    let debounceTimer = null;
    function debouncedDetection() {
        if (debounceTimer) {
            clearTimeout(debounceTimer);
        }
        debounceTimer = setTimeout(scheduleDetection, DETECTION_DEBOUNCE_MS);
    }
    
    /**
     * Set up event listeners for dynamic color changes
     */
    function setupListeners() {
        // Listen for scroll events
        window.addEventListener('scroll', debouncedDetection, { passive: true });
        
        // Listen for resize events
        window.addEventListener('resize', debouncedDetection, { passive: true });
        
        // Listen for color scheme changes
        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', scheduleDetection);
        
        // Set up mutation observer for DOM changes
        mutationObserver = new MutationObserver((mutations) => {
            // Check if any relevant changes occurred
            let shouldDetect = false;
            
            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    // Check for added/removed meta tags or header elements
                    for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            if (node.tagName === 'META' || node.tagName === 'LINK' ||
                                node.tagName === 'HEADER' || node.tagName === 'NAV') {
                                shouldDetect = true;
                                break;
                            }
                        }
                    }
                } else if (mutation.type === 'attributes') {
                    // Check for style or class changes on relevant elements
                    const target = mutation.target;
                    if (target.tagName === 'META' || target.tagName === 'BODY' || 
                        target.tagName === 'HTML' || target.tagName === 'HEADER' ||
                        target.tagName === 'NAV') {
                        shouldDetect = true;
                    }
                }
                
                if (shouldDetect) break;
            }
            
            if (shouldDetect) {
                debouncedDetection();
            }
        });
        
        mutationObserver.observe(document.documentElement, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['style', 'class', 'content', 'media']
        });
    }
    
    /**
     * Initialize the theme color detector
     */
    function init() {
        // Do initial detection
        scheduleDetection();
        
        // Set up listeners for changes
        setupListeners();
    }
    
    // Export for use by Electron
    window.__swai_initThemeColorDetector = init;
    window.__swai_detectThemeColor = detectThemeColor;
    
    // Auto-init if DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();

