/**
 * ThrillWiki Client-Side Performance Monitoring
 *
 * This module provides comprehensive client-side performance metrics collection
 * and monitoring. It tracks:
 * - Page load performance (Navigation Timing API)
 * - Paint metrics (First Paint, First Contentful Paint)
 * - Layout shifts (Cumulative Layout Shift)
 * - Largest Contentful Paint
 * - First Input Delay
 * - Memory usage (Chrome only)
 * - Long tasks (> 50ms)
 * - Resource timing
 *
 * Usage:
 *   Include this script after DOMContentLoaded.
 *   Metrics are logged to console in development and can be sent to a backend endpoint.
 */

(function() {
    'use strict';

    // Configuration
    const CONFIG = {
        enableConsoleLogging: true,  // Log to console in development
        enableBeacon: false,          // Send metrics to backend (set endpoint below)
        beaconEndpoint: '/api/v1/metrics/performance/',
        memoryCheckInterval: 30000,   // Check memory every 30 seconds
        memoryWarningThreshold: 0.9,  // Warn at 90% memory usage
        longTaskThreshold: 50,        // Long task threshold in ms
        reportDelay: 3000,            // Delay before reporting metrics
    };

    // Metrics storage
    const metrics = {
        navigation: {},
        paint: {},
        resources: [],
        longTasks: [],
        layoutShifts: [],
        largestContentfulPaint: null,
        firstInputDelay: null,
        memory: [],
        errors: [],
    };

    /**
     * Collect Navigation Timing metrics
     */
    function collectNavigationTiming() {
        if (!performance.getEntriesByType) return;

        const navEntries = performance.getEntriesByType('navigation');
        if (navEntries.length === 0) return;

        const nav = navEntries[0];
        metrics.navigation = {
            // DNS lookup time
            dnsLookup: nav.domainLookupEnd - nav.domainLookupStart,
            // TCP connection time
            tcpConnect: nav.connectEnd - nav.connectStart,
            // TLS handshake time (if HTTPS)
            tlsHandshake: nav.secureConnectionStart > 0
                ? nav.connectEnd - nav.secureConnectionStart
                : 0,
            // Time to First Byte (TTFB)
            ttfb: nav.responseStart - nav.requestStart,
            // Response download time
            responseTime: nav.responseEnd - nav.responseStart,
            // DOM parsing time
            domParsing: nav.domContentLoadedEventEnd - nav.domInteractive,
            // DOM Content Loaded
            domContentLoaded: nav.domContentLoadedEventEnd - nav.startTime,
            // Full page load
            loadComplete: nav.loadEventEnd - nav.startTime,
            // Redirect time (if any)
            redirectTime: nav.redirectEnd - nav.redirectStart,
            // Transfer size
            transferSize: nav.transferSize,
            // Encoded body size
            encodedBodySize: nav.encodedBodySize,
            // Decoded body size
            decodedBodySize: nav.decodedBodySize,
        };
    }

    /**
     * Collect Paint Timing metrics
     */
    function collectPaintTiming() {
        if (!performance.getEntriesByType) return;

        const paintEntries = performance.getEntriesByType('paint');
        paintEntries.forEach(entry => {
            if (entry.name === 'first-paint') {
                metrics.paint.firstPaint = entry.startTime;
            } else if (entry.name === 'first-contentful-paint') {
                metrics.paint.firstContentfulPaint = entry.startTime;
            }
        });
    }

    /**
     * Observe Largest Contentful Paint
     */
    function observeLCP() {
        if (!('PerformanceObserver' in window)) return;

        try {
            const lcpObserver = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                const lastEntry = entries[entries.length - 1];
                metrics.largestContentfulPaint = {
                    time: lastEntry.startTime,
                    element: lastEntry.element ? lastEntry.element.tagName : 'unknown',
                    size: lastEntry.size,
                    url: lastEntry.url || null,
                };
            });
            lcpObserver.observe({ type: 'largest-contentful-paint', buffered: true });
        } catch (e) {
            // LCP not supported
        }
    }

    /**
     * Observe First Input Delay
     */
    function observeFID() {
        if (!('PerformanceObserver' in window)) return;

        try {
            const fidObserver = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                entries.forEach(entry => {
                    if (!metrics.firstInputDelay) {
                        metrics.firstInputDelay = {
                            delay: entry.processingStart - entry.startTime,
                            eventType: entry.name,
                            target: entry.target ? entry.target.tagName : 'unknown',
                        };
                    }
                });
            });
            fidObserver.observe({ type: 'first-input', buffered: true });
        } catch (e) {
            // FID not supported
        }
    }

    /**
     * Observe Cumulative Layout Shift
     */
    function observeCLS() {
        if (!('PerformanceObserver' in window)) return;

        let clsValue = 0;
        let sessionValue = 0;
        let sessionEntries = [];

        try {
            const clsObserver = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                entries.forEach(entry => {
                    // Only count layout shifts without recent user input
                    if (!entry.hadRecentInput) {
                        const firstSessionEntry = sessionEntries[0];
                        const lastSessionEntry = sessionEntries[sessionEntries.length - 1];

                        // Start a new session if gap > 1 second or session > 5 seconds
                        if (sessionValue &&
                            (entry.startTime - lastSessionEntry.startTime > 1000 ||
                             entry.startTime - firstSessionEntry.startTime > 5000)) {
                            clsValue = Math.max(clsValue, sessionValue);
                            sessionValue = entry.value;
                            sessionEntries = [entry];
                        } else {
                            sessionValue += entry.value;
                            sessionEntries.push(entry);
                        }

                        metrics.layoutShifts.push({
                            value: entry.value,
                            time: entry.startTime,
                            sources: entry.sources ? entry.sources.length : 0,
                        });
                    }
                });
            });
            clsObserver.observe({ type: 'layout-shift', buffered: true });
        } catch (e) {
            // CLS not supported
        }
    }

    /**
     * Observe Long Tasks
     */
    function observeLongTasks() {
        if (!('PerformanceObserver' in window)) return;

        try {
            const longTaskObserver = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                entries.forEach(entry => {
                    if (entry.duration > CONFIG.longTaskThreshold) {
                        metrics.longTasks.push({
                            duration: entry.duration,
                            startTime: entry.startTime,
                            name: entry.name,
                        });
                    }
                });
            });
            longTaskObserver.observe({ entryTypes: ['longtask'] });
        } catch (e) {
            // Long tasks not supported
        }
    }

    /**
     * Monitor memory usage (Chrome only)
     */
    function monitorMemory() {
        if (!performance.memory) return;

        const checkMemory = () => {
            const memoryInfo = performance.memory;
            const usedRatio = memoryInfo.usedJSHeapSize / memoryInfo.jsHeapSizeLimit;

            const memorySnapshot = {
                timestamp: Date.now(),
                usedHeapSize: memoryInfo.usedJSHeapSize,
                totalHeapSize: memoryInfo.totalJSHeapSize,
                heapSizeLimit: memoryInfo.jsHeapSizeLimit,
                usedRatio: usedRatio,
            };

            metrics.memory.push(memorySnapshot);

            // Keep only last 10 snapshots
            if (metrics.memory.length > 10) {
                metrics.memory.shift();
            }

            // Warn if memory usage is high
            if (usedRatio > CONFIG.memoryWarningThreshold) {
                console.warn('ThrillWiki Performance: High memory usage detected', {
                    usedPercent: (usedRatio * 100).toFixed(1) + '%',
                    usedMB: (memoryInfo.usedJSHeapSize / 1024 / 1024).toFixed(2) + ' MB',
                    limitMB: (memoryInfo.jsHeapSizeLimit / 1024 / 1024).toFixed(2) + ' MB',
                });
            }
        };

        // Initial check
        checkMemory();

        // Periodic checks
        setInterval(checkMemory, CONFIG.memoryCheckInterval);
    }

    /**
     * Collect resource timing
     */
    function collectResourceTiming() {
        if (!performance.getEntriesByType) return;

        const resources = performance.getEntriesByType('resource');
        metrics.resources = resources.slice(0, 50).map(resource => ({
            name: resource.name.split('?')[0], // Remove query params
            type: resource.initiatorType,
            duration: resource.duration,
            transferSize: resource.transferSize,
            startTime: resource.startTime,
        }));
    }

    /**
     * Generate performance summary
     */
    function generateSummary() {
        const summary = {
            timestamp: new Date().toISOString(),
            url: window.location.href,
            userAgent: navigator.userAgent,
            navigation: metrics.navigation,
            paint: metrics.paint,
            lcp: metrics.largestContentfulPaint,
            fid: metrics.firstInputDelay,
            cls: metrics.layoutShifts.reduce((sum, shift) => sum + shift.value, 0),
            longTaskCount: metrics.longTasks.length,
            longTaskTotalTime: metrics.longTasks.reduce((sum, task) => sum + task.duration, 0),
            resourceCount: metrics.resources.length,
            totalTransferSize: metrics.resources.reduce((sum, r) => sum + (r.transferSize || 0), 0),
        };

        // Calculate Web Vitals ratings
        summary.webVitals = {
            lcp: rateMetric('lcp', summary.lcp?.time),
            fid: rateMetric('fid', summary.fid?.delay),
            cls: rateMetric('cls', summary.cls),
        };

        return summary;
    }

    /**
     * Rate a metric according to Web Vitals thresholds
     */
    function rateMetric(metric, value) {
        if (value === null || value === undefined) return 'unknown';

        const thresholds = {
            lcp: { good: 2500, needsImprovement: 4000 },
            fid: { good: 100, needsImprovement: 300 },
            cls: { good: 0.1, needsImprovement: 0.25 },
        };

        const threshold = thresholds[metric];
        if (!threshold) return 'unknown';

        if (value <= threshold.good) return 'good';
        if (value <= threshold.needsImprovement) return 'needs-improvement';
        return 'poor';
    }

    /**
     * Log metrics to console
     */
    function logMetrics() {
        if (!CONFIG.enableConsoleLogging) return;

        const summary = generateSummary();

        console.group('ThrillWiki Performance Metrics');
        console.log('Page Load:', summary.navigation.loadComplete?.toFixed(0) + 'ms');
        console.log('TTFB:', summary.navigation.ttfb?.toFixed(0) + 'ms');
        console.log('First Paint:', summary.paint.firstPaint?.toFixed(0) + 'ms');
        console.log('First Contentful Paint:', summary.paint.firstContentfulPaint?.toFixed(0) + 'ms');
        console.log('Largest Contentful Paint:', summary.lcp?.time?.toFixed(0) + 'ms', `(${summary.webVitals.lcp})`);
        console.log('First Input Delay:', summary.fid?.delay?.toFixed(0) + 'ms', `(${summary.webVitals.fid})`);
        console.log('Cumulative Layout Shift:', summary.cls?.toFixed(3), `(${summary.webVitals.cls})`);
        console.log('Long Tasks:', summary.longTaskCount, `(${summary.longTaskTotalTime.toFixed(0)}ms total)`);
        console.log('Resources:', summary.resourceCount, `(${(summary.totalTransferSize / 1024).toFixed(0)}KB)`);
        console.groupEnd();
    }

    /**
     * Send metrics to backend
     */
    function sendMetrics() {
        if (!CONFIG.enableBeacon || !navigator.sendBeacon) return;

        const summary = generateSummary();
        const blob = new Blob([JSON.stringify(summary)], { type: 'application/json' });

        try {
            navigator.sendBeacon(CONFIG.beaconEndpoint, blob);
        } catch (e) {
            console.warn('Failed to send performance metrics:', e);
        }
    }

    /**
     * Initialize performance monitoring
     */
    function init() {
        // Start observers early
        observeLCP();
        observeFID();
        observeCLS();
        observeLongTasks();

        // Collect metrics after page load
        window.addEventListener('load', () => {
            // Delay to ensure all metrics are available
            setTimeout(() => {
                collectNavigationTiming();
                collectPaintTiming();
                collectResourceTiming();
                logMetrics();
                sendMetrics();
            }, CONFIG.reportDelay);
        });

        // Start memory monitoring
        monitorMemory();

        // Report on page unload
        window.addEventListener('visibilitychange', () => {
            if (document.visibilityState === 'hidden') {
                sendMetrics();
            }
        });
    }

    // Expose API
    window.ThrillWikiPerformance = {
        getMetrics: () => metrics,
        getSummary: generateSummary,
        logMetrics: logMetrics,
    };

    // Initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
