/**
 * Performance Metrics Collection and Analysis
 * 
 * Comprehensive performance metrics collection for zanzibar-graph
 * performance testing with statistical analysis and reporting.
 */

export class PerformanceMetrics {
  constructor() {
    this.metrics = new Map();
    this.startTime = Date.now();
    this.testCount = 0;
  }

  /**
   * Record a performance metric
   */
  record(testName, data) {
    if (!this.metrics.has(testName)) {
      this.metrics.set(testName, []);
    }
    
    const metric = {
      timestamp: Date.now(),
      testName,
      data,
      testId: ++this.testCount
    };
    
    this.metrics.get(testName).push(metric);
  }

  /**
   * Get metrics for a specific test
   */
  getMetrics(testName) {
    return this.metrics.get(testName) || [];
  }

  /**
   * Get all metrics
   */
  getAllMetrics() {
    const allMetrics = {};
    for (const [testName, metrics] of this.metrics) {
      allMetrics[testName] = metrics;
    }
    return allMetrics;
  }

  /**
   * Calculate statistical summary for a test
   */
  calculateSummary(testName) {
    const metrics = this.getMetrics(testName);
    if (metrics.length === 0) return null;

    const values = metrics.map(m => m.data);
    
    // Extract numeric values for statistical analysis
    const numericValues = this._extractNumericValues(values);
    
    if (numericValues.length === 0) return null;

    return {
      count: numericValues.length,
      min: Math.min(...numericValues),
      max: Math.max(...numericValues),
      mean: this._calculateMean(numericValues),
      median: this._calculateMedian(numericValues),
      p95: this._calculatePercentile(numericValues, 95),
      p99: this._calculatePercentile(numericValues, 99),
      stdDev: this._calculateStdDev(numericValues),
      variance: this._calculateVariance(numericValues)
    };
  }

  /**
   * Generate comprehensive performance report
   */
  generateReport() {
    const report = {
      summary: this._generateSummary(),
      testResults: {},
      performanceTargets: this._getPerformanceTargets(),
      recommendations: this._generateRecommendations(),
      generatedAt: new Date().toISOString(),
      duration: Date.now() - this.startTime
    };

    // Generate test-specific results
    for (const [testName, metrics] of this.metrics) {
      report.testResults[testName] = {
        summary: this.calculateSummary(testName),
        rawData: metrics,
        analysis: this._analyzeTest(testName, metrics)
      };
    }

    return report;
  }

  /**
   * Generate overall summary
   */
  _generateSummary() {
    const allMetrics = this.getAllMetrics();
    const totalTests = Object.keys(allMetrics).length;
    
    // Calculate overall performance metrics
    let totalLatency = 0;
    let totalMemory = 0;
    let totalQueries = 0;
    let testCount = 0;

    for (const [testName, metrics] of Object.entries(allMetrics)) {
      for (const metric of metrics) {
        if (metric.data.avgLatency) {
          totalLatency += metric.data.avgLatency;
          testCount++;
        }
        if (metric.data.memoryUsed) {
          totalMemory += metric.data.memoryUsed;
        }
        if (metric.data.queryCount) {
          totalQueries += metric.data.queryCount;
        }
      }
    }

    return {
      totalTests,
      avgLatency: testCount > 0 ? totalLatency / testCount : 0,
      totalMemoryUsage: totalMemory,
      totalQueries,
      testCount,
      duration: Date.now() - this.startTime
    };
  }

  /**
   * Get performance targets
   */
  _getPerformanceTargets() {
    return {
      latency: {
        avg: 100, // ms
        p95: 200, // ms
        p99: 500  // ms
      },
      memory: {
        small: 512,   // MB
        medium: 1024, // MB
        large: 2048,  // MB
        enterprise: 4096 // MB
      },
      throughput: {
        qps: 1000, // queries per second
        tps: 500   // transactions per second
      },
      cache: {
        hitRate: 0.8, // 80%
        evictionRate: 0.1 // 10%
      }
    };
  }

  /**
   * Generate performance recommendations
   */
  _generateRecommendations() {
    const recommendations = [];
    const summary = this._generateSummary();
    const targets = this._getPerformanceTargets();

    // Latency recommendations
    if (summary.avgLatency > targets.latency.avg) {
      recommendations.push({
        type: 'latency',
        severity: 'high',
        message: `Average latency ${summary.avgLatency}ms exceeds target ${targets.latency.avg}ms`,
        suggestion: 'Consider optimizing authorization logic or increasing cache size'
      });
    }

    // Memory recommendations
    if (summary.totalMemoryUsage > targets.memory.medium) {
      recommendations.push({
        type: 'memory',
        severity: 'medium',
        message: `Memory usage ${summary.totalMemoryUsage}MB exceeds target ${targets.memory.medium}MB`,
        suggestion: 'Consider implementing memory optimization or increasing heap size'
      });
    }

    // Throughput recommendations
    if (summary.totalQueries > 0) {
      const avgQPS = (summary.totalQueries / summary.duration) * 1000;
      if (avgQPS < targets.throughput.qps * 0.8) {
        recommendations.push({
          type: 'throughput',
          severity: 'medium',
          message: `Average QPS ${avgQPS} below target ${targets.throughput.qps}`,
          suggestion: 'Consider optimizing query performance or increasing concurrency'
        });
      }
    }

    return recommendations;
  }

  /**
   * Analyze specific test results
   */
  _analyzeTest(testName, metrics) {
    const analysis = {
      performance: 'good',
      issues: [],
      suggestions: []
    };

    // Analyze based on test type
    switch (testName) {
      case 'graph_loading':
        this._analyzeGraphLoading(metrics, analysis);
        break;
      case 'authorization_qps':
        this._analyzeAuthorizationQPS(metrics, analysis);
        break;
      case 'memory_leak_test':
        this._analyzeMemoryLeak(metrics, analysis);
        break;
      case 'cache_performance':
        this._analyzeCachePerformance(metrics, analysis);
        break;
      default:
        this._analyzeGeneric(metrics, analysis);
    }

    return analysis;
  }

  /**
   * Analyze graph loading performance
   */
  _analyzeGraphLoading(metrics, analysis) {
    for (const metric of metrics) {
      const data = metric.data;
      
      if (data.loadTime > 30000) {
        analysis.issues.push('Graph loading time exceeds 30s limit');
        analysis.suggestions.push('Consider optimizing graph construction or using lazy loading');
        analysis.performance = 'poor';
      }
      
      if (data.memoryUsed > 1024) {
        analysis.issues.push('Memory usage exceeds 1GB limit');
        analysis.suggestions.push('Consider implementing memory optimization or reducing graph size');
        analysis.performance = 'poor';
      }
    }
  }

  /**
   * Analyze authorization QPS performance
   */
  _analyzeAuthorizationQPS(metrics, analysis) {
    for (const metric of metrics) {
      const data = metric.data;
      
      if (data.actualQPS < data.targetQPS * 0.8) {
        analysis.issues.push(`QPS ${data.actualQPS} below 80% of target ${data.targetQPS}`);
        analysis.suggestions.push('Consider optimizing authorization logic or increasing concurrency');
        analysis.performance = 'poor';
      }
      
      if (data.avgLatency > 100) {
        analysis.issues.push(`Average latency ${data.avgLatency}ms exceeds 100ms limit`);
        analysis.suggestions.push('Consider optimizing query performance or increasing cache size');
        analysis.performance = 'poor';
      }
      
      if (data.p95Latency > 200) {
        analysis.issues.push(`P95 latency ${data.p95Latency}ms exceeds 200ms limit`);
        analysis.suggestions.push('Consider optimizing worst-case performance or reducing query complexity');
        analysis.performance = 'poor';
      }
    }
  }

  /**
   * Analyze memory leak test results
   */
  _analyzeMemoryLeak(metrics, analysis) {
    for (const metric of metrics) {
      const data = metric.data;
      
      if (data.totalGrowth > 200) {
        analysis.issues.push(`Memory growth ${data.totalGrowth}MB exceeds 200MB limit`);
        analysis.suggestions.push('Investigate potential memory leaks in authorization logic');
        analysis.performance = 'poor';
      }
      
      if (data.totalGrowth > 100) {
        analysis.issues.push(`Memory growth ${data.totalGrowth}MB exceeds 100MB limit`);
        analysis.suggestions.push('Monitor memory usage and consider implementing garbage collection');
        analysis.performance = 'fair';
      }
    }
  }

  /**
   * Analyze cache performance
   */
  _analyzeCachePerformance(metrics, analysis) {
    for (const metric of metrics) {
      const data = metric.data;
      
      if (data.speedup < 1.5) {
        analysis.issues.push(`Cache speedup ${data.speedup}x below 1.5x threshold`);
        analysis.suggestions.push('Consider optimizing cache implementation or increasing cache size');
        analysis.performance = 'poor';
      }
      
      if (data.speedup < 2.0) {
        analysis.issues.push(`Cache speedup ${data.speedup}x below 2.0x threshold`);
        analysis.suggestions.push('Consider optimizing cache hit rate or cache eviction strategy');
        analysis.performance = 'fair';
      }
    }
  }

  /**
   * Generic analysis for unknown test types
   */
  _analyzeGeneric(metrics, analysis) {
    const summary = this.calculateSummary(metrics[0]?.testName);
    if (!summary) return;

    if (summary.mean > 1000) {
      analysis.issues.push(`Average performance ${summary.mean}ms exceeds 1000ms threshold`);
      analysis.suggestions.push('Consider optimizing performance or reducing complexity');
      analysis.performance = 'poor';
    }
  }

  /**
   * Extract numeric values from metric data
   */
  _extractNumericValues(values) {
    const numericValues = [];
    
    for (const value of values) {
      if (typeof value === 'number') {
        numericValues.push(value);
      } else if (typeof value === 'object' && value !== null) {
        // Extract numeric values from objects
        for (const [key, val] of Object.entries(value)) {
          if (typeof val === 'number') {
            numericValues.push(val);
          }
        }
      }
    }
    
    return numericValues;
  }

  /**
   * Calculate mean
   */
  _calculateMean(values) {
    return values.reduce((sum, val) => sum + val, 0) / values.length;
  }

  /**
   * Calculate median
   */
  _calculateMedian(values) {
    const sorted = [...values].sort((a, b) => a - b);
    const mid = Math.floor(sorted.length / 2);
    return sorted.length % 2 === 0 
      ? (sorted[mid - 1] + sorted[mid]) / 2 
      : sorted[mid];
  }

  /**
   * Calculate percentile
   */
  _calculatePercentile(values, percentile) {
    const sorted = [...values].sort((a, b) => a - b);
    const index = Math.ceil((percentile / 100) * sorted.length) - 1;
    return sorted[Math.max(0, index)];
  }

  /**
   * Calculate standard deviation
   */
  _calculateStdDev(values) {
    const mean = this._calculateMean(values);
    const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
    return Math.sqrt(variance);
  }

  /**
   * Calculate variance
   */
  _calculateVariance(values) {
    const mean = this._calculateMean(values);
    return values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
  }

  /**
   * Export metrics to JSON
   */
  exportToJSON() {
    return JSON.stringify(this.generateReport(), null, 2);
  }

  /**
   * Export metrics to CSV
   */
  exportToCSV() {
    const csv = [];
    csv.push('TestName,Timestamp,TestId,Data');
    
    for (const [testName, metrics] of this.metrics) {
      for (const metric of metrics) {
        csv.push(`${testName},${metric.timestamp},${metric.testId},"${JSON.stringify(metric.data)}"`);
      }
    }
    
    return csv.join('\n');
  }

  /**
   * Clear all metrics
   */
  clear() {
    this.metrics.clear();
    this.startTime = Date.now();
    this.testCount = 0;
  }

  /**
   * Get metrics for a specific time range
   */
  getMetricsInRange(startTime, endTime) {
    const filteredMetrics = new Map();
    
    for (const [testName, metrics] of this.metrics) {
      const filtered = metrics.filter(m => 
        m.timestamp >= startTime && m.timestamp <= endTime
      );
      if (filtered.length > 0) {
        filteredMetrics.set(testName, filtered);
      }
    }
    
    return filteredMetrics;
  }

  /**
   * Get performance trends over time
   */
  getPerformanceTrends(testName, windowSize = 1000) {
    const metrics = this.getMetrics(testName);
    if (metrics.length === 0) return null;

    const trends = [];
    const window = Math.min(windowSize, metrics.length);
    
    for (let i = window; i <= metrics.length; i++) {
      const windowMetrics = metrics.slice(i - window, i);
      const trend = this._calculateTrend(windowMetrics);
      trends.push({
        timestamp: metrics[i - 1].timestamp,
        trend: trend
      });
    }
    
    return trends;
  }

  /**
   * Calculate trend for a window of metrics
   */
  _calculateTrend(metrics) {
    if (metrics.length < 2) return null;

    const values = this._extractNumericValues(metrics.map(m => m.data));
    if (values.length < 2) return null;

    const firstHalf = values.slice(0, Math.floor(values.length / 2));
    const secondHalf = values.slice(Math.floor(values.length / 2));
    
    const firstMean = this._calculateMean(firstHalf);
    const secondMean = this._calculateMean(secondHalf);
    
    return {
      direction: secondMean > firstMean ? 'increasing' : 'decreasing',
      change: secondMean - firstMean,
      changePercent: ((secondMean - firstMean) / firstMean) * 100
    };
  }
}
