/**
 * Performance monitoring utility for tracking Core Web Vitals and custom metrics
 */

import { nextTick } from 'vue';

export interface PerformanceMetric {
    name: string;
    value: number;
    timestamp: number;
    route?: string;
    metadata?: Record<string, any>;
}

class PerformanceMonitor {
    private metrics: PerformanceMetric[] = [];
    private observers: PerformanceObserver[] = [];
    private routeStartTimes = new Map<string, number>();
    private cumulativeLayoutShift = 0;

    constructor() {
        this.initializeObservers();
    }

    /**
     * Initialize performance observers for Core Web Vitals
     */
    private initializeObservers() {
        // First Contentful Paint (FCP)
        if ('PerformanceObserver' in window) {
            const paintObserver = new PerformanceObserver((list) => {
                for (const entry of list.getEntries()) {
                    if (entry.name === 'first-contentful-paint') {
                        this.recordMetric('FCP', entry.startTime);
                    }
                }
            });

            try {
                paintObserver.observe({ entryTypes: ['paint'] });
                this.observers.push(paintObserver);
            } catch (_e) {
                console.warn('Paint observer not supported');
            }

            // Largest Contentful Paint (LCP)
            const lcpObserver = new PerformanceObserver((list) => {
                for (const entry of list.getEntries()) {
                    this.recordMetric('LCP', entry.startTime);
                }
            });

            try {
                lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
                this.observers.push(lcpObserver);
            } catch (_e) {
                console.warn('LCP observer not supported');
            }

            // First Input Delay (FID)
            try {
                const fidObserver = new PerformanceObserver((list) => {
                    for (const entry of list.getEntries()) {
                        this.recordMetric('FID', (entry as any).processingStart - entry.startTime);
                    }
                });
                fidObserver.observe({ entryTypes: ['first-input'] });
                this.observers.push(fidObserver);
            } catch (_e) {
                console.warn('FID observer not supported');
            }

            // Cumulative Layout Shift (CLS)
            try {
                const clsObserver = new PerformanceObserver((list) => {
                    for (const entry of list.getEntries()) {
                        if ((entry as any).hadRecentInput) continue;
                        this.cumulativeLayoutShift += (entry as any).value;
                        this.recordMetric('CLS', this.cumulativeLayoutShift);
                    }
                });
                clsObserver.observe({ entryTypes: ['layout-shift'] });
                this.observers.push(clsObserver);
            } catch (_e) {
                console.warn('CLS observer not supported');
            }

            // Time to First Byte (TTFB)
            try {
                const observer = new PerformanceObserver((list) => {
                    for (const entry of list.getEntries()) {
                        const navEntry = entry as PerformanceNavigationTiming;
                        this.recordMetric('TTFB', navEntry.responseStart - navEntry.fetchStart);
                    }
                });
                observer.observe({ entryTypes: ['navigation'] });
                this.observers.push(observer);
            } catch (_e) {
                console.warn('Navigation timing observer not supported');
            }
        }
    }

    /**
     * Record a performance metric
     */
    recordMetric(name: string, value: number, metadata?: Record<string, any>) {
        const metric: PerformanceMetric = {
            name,
            value,
            timestamp: Date.now(),
            route: this.getCurrentRoute(),
            metadata
        };

        this.metrics.push(metric);

        // Emit custom event for external tracking
        window.dispatchEvent(new CustomEvent('performance-metric', {
            detail: metric
        }));

        // Log to console in development
        if (import.meta.env.DEV) {
            console.log(`📊 ${name}: ${value.toFixed(2)}ms`, metadata);
        }
    }

    /**
     * Start timing a custom metric
     */
    startTiming(name: string): () => void {
        const startTime = performance.now();

        return () => {
            const duration = performance.now() - startTime;
            this.recordMetric(name, duration);
        };
    }

    /**
     * Measure async operation
     */
    async measureAsync<T>(name: string, operation: () => Promise<T>): Promise<T> {
        const startTime = performance.now();

        try {
            const result = await operation();
            const duration = performance.now() - startTime;
            this.recordMetric(name, duration, { success: true });
            return result;
        } catch (error) {
            const duration = performance.now() - startTime;
            this.recordMetric(name, duration, { success: false, error: String(error) });
            throw error;
        }
    }

    /**
     * Track route navigation performance
     */
    startRouteNavigation(routeName: string) {
        this.routeStartTimes.set(routeName, performance.now());
    }

    endRouteNavigation(routeName: string) {
        const startTime = this.routeStartTimes.get(routeName);
        if (startTime) {
            const duration = performance.now() - startTime;
            this.recordMetric('RouteNavigation', duration, { route: routeName });
            this.routeStartTimes.delete(routeName);
        }
    }

    /**
     * Track component mount time
     */
    trackComponentMount(componentName: string) {
        const startTime = performance.now();

        nextTick(() => {
            const duration = performance.now() - startTime;
            this.recordMetric('ComponentMount', duration, { component: componentName });
        });
    }

    /**
     * Track API call performance
     */
    trackApiCall(endpoint: string, method: string = 'GET') {
        const startTime = performance.now();

        return {
            end: (success: boolean = true, statusCode?: number) => {
                const duration = performance.now() - startTime;
                this.recordMetric('ApiCall', duration, {
                    endpoint,
                    method,
                    success,
                    statusCode
                });
            }
        };
    }

    /**
     * Get performance summary
     */
    getSummary() {
        const summary: Record<string, { count: number; avg: number; min: number; max: number }> = {};

        this.metrics.forEach(metric => {
            if (!summary[metric.name]) {
                summary[metric.name] = { count: 0, avg: 0, min: Infinity, max: -Infinity };
            }

            const s = summary[metric.name];
            s.count++;
            s.min = Math.min(s.min, metric.value);
            s.max = Math.max(s.max, metric.value);
        });

        // Calculate averages
        Object.keys(summary).forEach(key => {
            const metrics = this.metrics.filter(m => m.name === key);
            summary[key].avg = metrics.reduce((sum, m) => sum + m.value, 0) / metrics.length;
        });

        return summary;
    }

    /**
     * Get Core Web Vitals scores
     */
    getCoreWebVitals() {
        const getLatestMetric = (name: string) => {
            const metrics = this.metrics.filter(m => m.name === name);
            return metrics[metrics.length - 1]?.value || 0;
        };

        const fcp = getLatestMetric('FCP');
        const lcp = getLatestMetric('LCP');
        const fid = getLatestMetric('FID');
        const cls = getLatestMetric('CLS');
        const ttfb = getLatestMetric('TTFB');

        return {
            FCP: {
                value: fcp,
                score: fcp <= 1800 ? 'good' : fcp <= 3000 ? 'needs-improvement' : 'poor'
            },
            LCP: {
                value: lcp,
                score: lcp <= 2500 ? 'good' : lcp <= 4000 ? 'needs-improvement' : 'poor'
            },
            FID: {
                value: fid,
                score: fid <= 100 ? 'good' : fid <= 300 ? 'needs-improvement' : 'poor'
            },
            CLS: {
                value: cls,
                score: cls <= 0.1 ? 'good' : cls <= 0.25 ? 'needs-improvement' : 'poor'
            },
            TTFB: {
                value: ttfb,
                score: ttfb <= 800 ? 'good' : ttfb <= 1800 ? 'needs-improvement' : 'poor'
            }
        };
    }

    /**
     * Export metrics for analytics
     */
    exportMetrics() {
        return {
            metrics: [...this.metrics],
            summary: this.getSummary(),
            coreWebVitals: this.getCoreWebVitals(),
            timestamp: Date.now(),
            userAgent: navigator.userAgent,
            url: window.location.href
        };
    }

    /**
     * Clear metrics (useful for SPA route changes)
     */
    clearMetrics() {
        this.metrics = [];
    }

    /**
     * Get current route name
     */
    private getCurrentRoute(): string {
        return window.location.pathname;
    }

    /**
     * Cleanup observers
     */
    destroy() {
        this.observers.forEach(observer => observer.disconnect());
        this.observers = [];
    }
}

// Global performance monitor instance
const performanceMonitor = new PerformanceMonitor();

// Composable for Vue components
export function usePerformance() {
    return {
        recordMetric: performanceMonitor.recordMetric.bind(performanceMonitor),
        startTiming: performanceMonitor.startTiming.bind(performanceMonitor),
        measureAsync: performanceMonitor.measureAsync.bind(performanceMonitor),
        trackComponentMount: performanceMonitor.trackComponentMount.bind(performanceMonitor),
        trackApiCall: performanceMonitor.trackApiCall.bind(performanceMonitor),
        getSummary: performanceMonitor.getSummary.bind(performanceMonitor),
        getCoreWebVitals: performanceMonitor.getCoreWebVitals.bind(performanceMonitor),
        exportMetrics: performanceMonitor.exportMetrics.bind(performanceMonitor)
    };
}

// Resource timing analysis
export function analyzeResourceTiming() {
    const resources = performance.getEntriesByType('resource') as PerformanceResourceTiming[];

    const analysis = {
        totalResources: resources.length,
        totalSize: 0,
        totalDuration: 0,
        byType: {} as Record<string, { count: number; size: number; duration: number }>,
        slowestResources: [] as Array<{ name: string; duration: number; size: number }>
    };

    resources.forEach(resource => {
        const duration = resource.responseEnd - resource.requestStart;
        const size = resource.transferSize || 0;

        analysis.totalDuration += duration;
        analysis.totalSize += size;

        // Categorize by resource type
        const type = getResourceType(resource.name);
        if (!analysis.byType[type]) {
            analysis.byType[type] = { count: 0, size: 0, duration: 0 };
        }

        analysis.byType[type].count++;
        analysis.byType[type].size += size;
        analysis.byType[type].duration += duration;

        // Track slowest resources
        analysis.slowestResources.push({
            name: resource.name,
            duration,
            size
        });
    });

    // Sort slowest resources
    analysis.slowestResources.sort((a, b) => b.duration - a.duration);
    analysis.slowestResources = analysis.slowestResources.slice(0, 10);

    return analysis;
}

function getResourceType(url: string): string {
    if (url.includes('.js')) return 'javascript';
    if (url.includes('.css')) return 'stylesheet';
    if (url.match(/\.(png|jpg|jpeg|gif|svg|webp|avif)$/)) return 'image';
    if (url.match(/\.(woff|woff2|ttf|otf)$/)) return 'font';
    if (url.includes('/api/')) return 'api';
    return 'other';
}

// Budget monitoring
export function createPerformanceBudget(budgets: Record<string, number>) {
    return {
        check: () => {
            const vitals = performanceMonitor.getCoreWebVitals();
            const violations: Array<{ metric: string; actual: number; budget: number }> = [];

            Object.entries(budgets).forEach(([metric, budget]) => {
                const actual = vitals[metric as keyof typeof vitals]?.value || 0;
                if (actual > budget) {
                    violations.push({ metric, actual, budget });
                }
            });

            return violations;
        }
    };
}

export { performanceMonitor }; 