/**
 * Web Vitals Monitoring
 *
 * Tracks Core Web Vitals (LCP, FID, CLS) and sends metrics to Sentry.
 * Automatically integrates with Sentry Performance Monitoring.
 *
 * Core Web Vitals tracked:
 * - LCP (Largest Contentful Paint): Loading performance
 * - INP (Interaction to Next Paint): Overall responsiveness (replaces FID)
 * - CLS (Cumulative Layout Shift): Visual stability
 *
 * Also tracks:
 * - TTFB (Time to First Byte): Server response time
 * - FCP (First Contentful Paint): Initial render
 *
 * Note: FID is deprecated in favor of INP as of web-vitals v3
 *
 * Usage:
 * Import in your root layout or _app.tsx:
 * ```tsx
 * import { reportWebVitals } from '@/lib/monitoring/web-vitals';
 *
 * useEffect(() => {
 *   reportWebVitals();
 * }, []);
 * ```
 */

import type { Metric } from 'web-vitals';

/**
 * Web Vitals metric names
 * Note: FID is deprecated in favor of INP as of web-vitals v3
 */
export type WebVitalsMetric = 'CLS' | 'FCP' | 'LCP' | 'TTFB' | 'INP';

/**
 * Metric thresholds for performance classification
 * Based on Google's Core Web Vitals recommendations
 */
const METRIC_THRESHOLDS = {
  // Largest Contentful Paint (ms)
  LCP: {
    good: 2500,
    needsImprovement: 4000,
  },
  // Cumulative Layout Shift (score)
  CLS: {
    good: 0.1,
    needsImprovement: 0.25,
  },
  // Time to First Byte (ms)
  TTFB: {
    good: 800,
    needsImprovement: 1800,
  },
  // First Contentful Paint (ms)
  FCP: {
    good: 1800,
    needsImprovement: 3000,
  },
  // Interaction to Next Paint (ms) - replaces FID
  INP: {
    good: 200,
    needsImprovement: 500,
  },
} as const;

/**
 * Classification of metric performance
 */
type MetricRating = 'good' | 'needs-improvement' | 'poor';

/**
 * Get performance rating for a metric
 */
function getMetricRating(metric: Metric): MetricRating {
  const { name, value } = metric;
  const thresholds = METRIC_THRESHOLDS[name as WebVitalsMetric];

  if (!thresholds) {
    return 'good';
  }

  if (value <= thresholds.good) {
    return 'good';
  }

  if (value <= thresholds.needsImprovement) {
    return 'needs-improvement';
  }

  return 'poor';
}

/**
 * Send metric to Sentry
 */
async function sendToSentry(metric: Metric): Promise<void> {
  try {
    // Dynamic import to avoid build errors when @sentry/nextjs is not installed
    const sentryModuleName = '@sentry/nextjs';
    const Sentry = await import(/* webpackIgnore: true */ sentryModuleName);

    const rating = getMetricRating(metric);

    // Send as measurement to Sentry
    Sentry.setMeasurement(metric.name, metric.value, 'millisecond');

    // Add breadcrumb for tracking
    Sentry.addBreadcrumb({
      category: 'web-vitals',
      message: `${metric.name}: ${metric.value.toFixed(2)}`,
      level: rating === 'poor' ? 'warning' : 'info',
      data: {
        value: metric.value,
        rating,
        id: metric.id,
        navigationType: metric.navigationType,
      },
    });

    // If metric is poor, capture as a performance issue
    if (rating === 'poor') {
      Sentry.captureMessage(`Poor ${metric.name} performance detected`, {
        level: 'warning',
        tags: {
          metric: metric.name,
          rating,
        },
        contexts: {
          'web-vitals': {
            name: metric.name,
            value: metric.value,
            rating,
            id: metric.id,
            navigationType: metric.navigationType,
          },
        },
      });
    }
  } catch {
    // Sentry not available, silently skip
    if (process.env.NODE_ENV === 'development') {
      console.debug('Sentry not available for web vitals reporting');
    }
  }
}

/**
 * Send metric to analytics (can be extended for GA4, etc.)
 */
function sendToAnalytics(metric: Metric): void {
  // Send to Google Analytics 4 if available
  if (typeof window !== 'undefined' && 'gtag' in window) {
    const gtag = (window as unknown as { gtag: (...args: unknown[]) => void }).gtag;
    gtag('event', metric.name, {
      value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
      event_category: 'Web Vitals',
      event_label: metric.id,
      non_interaction: true,
    });
  }

  // Log in development
  if (process.env.NODE_ENV === 'development') {
    console.debug('[Web Vitals]', {
      name: metric.name,
      value: metric.value.toFixed(2),
      rating: getMetricRating(metric),
      id: metric.id,
    });
  }
}

/**
 * Handle web vitals metric
 */
function handleMetric(metric: Metric): void {
  // Send to Sentry
  void sendToSentry(metric);

  // Send to Analytics
  sendToAnalytics(metric);
}

/**
 * Initialize Web Vitals reporting
 *
 * This function should be called once in your application,
 * typically in the root layout or _app component.
 *
 * @example
 * ```tsx
 * import { reportWebVitals } from '@/lib/monitoring/web-vitals';
 *
 * export default function RootLayout({ children }) {
 *   useEffect(() => {
 *     reportWebVitals();
 *   }, []);
 *
 *   return <html>{children}</html>;
 * }
 * ```
 */
export function reportWebVitals(): void {
  // Only run in browser
  if (typeof window === 'undefined') {
    return;
  }

  // Dynamic import web-vitals to avoid SSR issues
  import('web-vitals')
    .then(({ onCLS, onFCP, onLCP, onTTFB, onINP }) => {
      // Track Core Web Vitals
      onCLS(handleMetric);
      onLCP(handleMetric);

      // Track additional metrics
      onFCP(handleMetric);
      onTTFB(handleMetric);
      onINP(handleMetric);
    })
    .catch(() => {
      if (process.env.NODE_ENV === 'development') {
        console.debug('web-vitals package not available');
      }
    });
}

/**
 * Get current Web Vitals metrics
 *
 * Useful for debugging or displaying metrics in the UI
 *
 * @returns Promise that resolves with current metrics
 */
export async function getCurrentMetrics(): Promise<Metric[]> {
  if (typeof window === 'undefined') {
    return [];
  }

  try {
    const { onCLS, onFCP, onLCP, onTTFB, onINP } = await import('web-vitals');

    const metrics: Metric[] = [];

    const collectMetric = (metric: Metric) => {
      metrics.push(metric);
    };

    // Collect all metrics
    onCLS(collectMetric, { reportAllChanges: true });
    onFCP(collectMetric);
    onLCP(collectMetric, { reportAllChanges: true });
    onTTFB(collectMetric);
    onINP(collectMetric, { reportAllChanges: true });

    return metrics;
  } catch {
    return [];
  }
}

/**
 * Export metric thresholds for use in tests or UI
 */
export { METRIC_THRESHOLDS };
