import { logPerformance } from './logger';
import { startTransaction, addBreadcrumb } from './sentry';

/**
 * Performance monitoring utilities
 */

export class PerformanceMonitor {
  private startTime: number;
  private endTime?: number;
  private name: string;
  private sentryTransaction: any;

  constructor(name: string, operation: string = 'custom') {
    this.name = name;
    this.startTime = Date.now();
    this.sentryTransaction = startTransaction(name, operation);
    
    addBreadcrumb(`Started ${name}`, 'performance', 'info');
  }

  /**
   * Mark the end of the performance measurement
   */
  end(additionalMetrics?: Record<string, number>) {
    this.endTime = Date.now();
    const duration = this.endTime - this.startTime;

    // Log to our custom logger
    logPerformance({
      operation: this.name,
      duration,
      additionalMetrics
    });

    // Finish Sentry transaction
    if (this.sentryTransaction) {
      this.sentryTransaction.setTag('duration', duration.toString());
      if (additionalMetrics) {
        Object.entries(additionalMetrics).forEach(([key, value]) => {
          this.sentryTransaction.setTag(key, value.toString());
        });
      }
      this.sentryTransaction.finish();
    }

    addBreadcrumb(`Completed ${this.name} in ${duration}ms`, 'performance', 'info');

    return duration;
  }

  /**
   * Get current duration without ending the measurement
   */
  getCurrentDuration(): number {
    return Date.now() - this.startTime;
  }
}

/**
 * Monitor database query performance
 */
export class DatabaseMonitor {
  private static instance: DatabaseMonitor;
  private queryTimes: Map<string, number[]> = new Map();

  static getInstance(): DatabaseMonitor {
    if (!DatabaseMonitor.instance) {
      DatabaseMonitor.instance = new DatabaseMonitor();
    }
    return DatabaseMonitor.instance;
  }

  /**
   * Track a database query
   */
  trackQuery(query: string, duration: number, table?: string) {
    const key = table || 'unknown';
    if (!this.queryTimes.has(key)) {
      this.queryTimes.set(key, []);
    }
    
    this.queryTimes.get(key)!.push(duration);

    // Log slow queries
    if (duration > 1000) { // Queries over 1 second
      console.warn(`Slow query detected: ${query} took ${duration}ms`);
      addBreadcrumb(`Slow query: ${query.substring(0, 100)}...`, 'database', 'warning', {
        duration,
        table
      });
    }

    // Clean up old metrics (keep only last 100 per table)
    const times = this.queryTimes.get(key)!;
    if (times.length > 100) {
      times.splice(0, times.length - 100);
    }
  }

  /**
   * Get average query time for a table
   */
  getAverageQueryTime(table: string): number {
    const times = this.queryTimes.get(table);
    if (!times || times.length === 0) return 0;
    
    return times.reduce((sum, time) => sum + time, 0) / times.length;
  }

  /**
   * Get performance metrics for all tables
   */
  getMetrics(): Record<string, { avg: number; max: number; count: number }> {
    const metrics: Record<string, { avg: number; max: number; count: number }> = {};
    
    for (const [table, times] of this.queryTimes.entries()) {
      if (times.length === 0) continue;
      
      metrics[table] = {
        avg: times.reduce((sum, time) => sum + time, 0) / times.length,
        max: Math.max(...times),
        count: times.length
      };
    }
    
    return metrics;
  }
}

/**
 * Monitor API endpoint performance
 */
export class APIMonitor {
  private static metrics: Map<string, { times: number[]; errors: number }> = new Map();

  /**
   * Track API response time
   */
  static trackEndpoint(endpoint: string, method: string, duration: number, statusCode: number) {
    const key = `${method} ${endpoint}`;
    
    if (!this.metrics.has(key)) {
      this.metrics.set(key, { times: [], errors: 0 });
    }
    
    const metric = this.metrics.get(key)!;
    metric.times.push(duration);
    
    if (statusCode >= 400) {
      metric.errors++;
    }

    // Clean up old metrics
    if (metric.times.length > 100) {
      metric.times.splice(0, metric.times.length - 100);
    }

    // Log slow API calls
    if (duration > 5000) { // API calls over 5 seconds
      console.warn(`Slow API call: ${key} took ${duration}ms`);
      addBreadcrumb(`Slow API call: ${key}`, 'http', 'warning', {
        duration,
        statusCode
      });
    }
  }

  /**
   * Get API performance metrics
   */
  static getMetrics(): Record<string, { avg: number; max: number; count: number; errorRate: number }> {
    const metrics: Record<string, { avg: number; max: number; count: number; errorRate: number }> = {};
    
    for (const [endpoint, data] of this.metrics.entries()) {
      if (data.times.length === 0) continue;
      
      metrics[endpoint] = {
        avg: data.times.reduce((sum, time) => sum + time, 0) / data.times.length,
        max: Math.max(...data.times),
        count: data.times.length,
        errorRate: data.errors / data.times.length
      };
    }
    
    return metrics;
  }
}

/**
 * Memory usage monitoring
 */
export class MemoryMonitor {
  private static lastCheck = Date.now();
  private static samples: Array<{ timestamp: number; usage: NodeJS.MemoryUsage }> = [];

  /**
   * Take a memory usage sample
   */
  static sample() {
    const now = Date.now();
    const usage = process.memoryUsage();
    
    this.samples.push({ timestamp: now, usage });
    
    // Keep only last 100 samples
    if (this.samples.length > 100) {
      this.samples.splice(0, this.samples.length - 100);
    }

    // Log memory warning if usage is high
    const heapUsedMB = usage.heapUsed / 1024 / 1024;
    if (heapUsedMB > 512) { // Over 512MB
      console.warn(`High memory usage: ${heapUsedMB.toFixed(2)}MB`);
      addBreadcrumb(`High memory usage: ${heapUsedMB.toFixed(2)}MB`, 'memory', 'warning', {
        heapUsed: usage.heapUsed,
        heapTotal: usage.heapTotal,
        external: usage.external
      });
    }

    this.lastCheck = now;
  }

  /**
   * Get memory usage trends
   */
  static getTrends(): {
    current: NodeJS.MemoryUsage;
    average: Partial<NodeJS.MemoryUsage>;
    peak: Partial<NodeJS.MemoryUsage>;
  } {
    if (this.samples.length === 0) {
      return {
        current: process.memoryUsage(),
        average: {},
        peak: {}
      };
    }

    const current = this.samples[this.samples.length - 1].usage;
    
    // Calculate averages
    const avgHeapUsed = this.samples.reduce((sum, s) => sum + s.usage.heapUsed, 0) / this.samples.length;
    const avgHeapTotal = this.samples.reduce((sum, s) => sum + s.usage.heapTotal, 0) / this.samples.length;
    
    // Find peaks
    const peakHeapUsed = Math.max(...this.samples.map(s => s.usage.heapUsed));
    const peakHeapTotal = Math.max(...this.samples.map(s => s.usage.heapTotal));

    return {
      current,
      average: {
        heapUsed: avgHeapUsed,
        heapTotal: avgHeapTotal
      },
      peak: {
        heapUsed: peakHeapUsed,
        heapTotal: peakHeapTotal
      }
    };
  }

  /**
   * Start automatic memory monitoring
   */
  static startMonitoring(intervalMs: number = 60000) { // Default: every minute
    setInterval(() => {
      this.sample();
    }, intervalMs);
  }
}

/**
 * Web Vitals monitoring for the frontend
 */
export const WebVitalsMonitor = {
  /**
   * Monitor Core Web Vitals
   */
  initWebVitals() {
    if (typeof window === 'undefined') return;

    // Monitor Largest Contentful Paint (LCP)
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (entry.entryType === 'largest-contentful-paint') {
          addBreadcrumb(`LCP: ${entry.startTime.toFixed(2)}ms`, 'performance', 'info');
          
          if (entry.startTime > 2500) { // LCP > 2.5s is poor
            console.warn(`Poor LCP: ${entry.startTime.toFixed(2)}ms`);
          }
        }
      }
    });

    observer.observe({ entryTypes: ['largest-contentful-paint'] });

    // Monitor First Input Delay (FID)
    const fidObserver = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (entry.entryType === 'first-input') {
          const fid = entry.processingStart - entry.startTime;
          addBreadcrumb(`FID: ${fid.toFixed(2)}ms`, 'performance', 'info');
          
          if (fid > 100) { // FID > 100ms is poor
            console.warn(`Poor FID: ${fid.toFixed(2)}ms`);
          }
        }
      }
    });

    fidObserver.observe({ entryTypes: ['first-input'] });

    // Monitor Cumulative Layout Shift (CLS)
    let clsValue = 0;
    const clsObserver = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (!entry.hadRecentInput) {
          clsValue += entry.value;
        }
      }
      
      if (clsValue > 0.1) { // CLS > 0.1 is poor
        console.warn(`Poor CLS: ${clsValue.toFixed(4)}`);
      }
    });

    clsObserver.observe({ entryTypes: ['layout-shift'] });
  },

  /**
   * Monitor page load performance
   */
  trackPageLoad() {
    if (typeof window === 'undefined') return;

    window.addEventListener('load', () => {
      setTimeout(() => {
        const perfData = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
        
        const metrics = {
          domContentLoaded: perfData.domContentLoadedEventEnd - perfData.domContentLoadedEventStart,
          domComplete: perfData.domComplete - perfData.navigationStart,
          loadComplete: perfData.loadEventEnd - perfData.navigationStart,
          firstByte: perfData.responseStart - perfData.requestStart
        };

        addBreadcrumb('Page load metrics', 'performance', 'info', metrics);

        // Log slow page loads
        if (metrics.loadComplete > 3000) { // Over 3 seconds
          console.warn(`Slow page load: ${metrics.loadComplete}ms`);
        }
      }, 0);
    });
  }
};

/**
 * Utility functions
 */
export function measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T> {
  const monitor = new PerformanceMonitor(name, 'async');
  
  return fn()
    .then(result => {
      monitor.end();
      return result;
    })
    .catch(error => {
      monitor.end();
      throw error;
    });
}

export function measureSync<T>(name: string, fn: () => T): T {
  const monitor = new PerformanceMonitor(name, 'sync');
  
  try {
    const result = fn();
    monitor.end();
    return result;
  } catch (error) {
    monitor.end();
    throw error;
  }
}

// Start memory monitoring automatically
MemoryMonitor.startMonitoring();

// Export all monitors
export { DatabaseMonitor, APIMonitor, MemoryMonitor };