/**
 * Performance Benchmarking for Collaboration Layer
 * Measures sync latency, memory usage, and operation throughput
 */

export interface BenchmarkResult {
  name: string;
  duration: number;
  timestamp: Date;
  metrics: Record<string, number>;
}

export interface SyncLatencyMetrics {
  min: number;
  max: number;
  avg: number;
  p50: number;
  p95: number;
  p99: number;
  samples: number;
}

export interface MemoryMetrics {
  heapUsed: number;
  heapTotal: number;
  external: number;
  rss: number;
}

/**
 * Performance benchmark for collaboration features
 */
export class CollaborationBenchmark {
  private results: BenchmarkResult[] = [];
  private syncLatencies: number[] = [];
  private operationStartTimes: Map<string, number> = new Map();
  private isEnabled: boolean = false;

  constructor(enabled: boolean = false) {
    this.isEnabled = enabled;
  }

  /**
   * Enable benchmarking
   */
  enable(): void {
    this.isEnabled = true;
  }

  /**
   * Disable benchmarking
   */
  disable(): void {
    this.isEnabled = false;
  }

  /**
   * Start timing an operation
   */
  startOperation(operationId: string): void {
    if (!this.isEnabled) return;
    this.operationStartTimes.set(operationId, performance.now());
  }

  /**
   * End timing an operation and record result
   */
  endOperation(
    operationId: string,
    metrics?: Record<string, number>
  ): BenchmarkResult | null {
    if (!this.isEnabled) return null;

    const startTime = this.operationStartTimes.get(operationId);
    if (startTime === undefined) {
      return null;
    }

    const duration = performance.now() - startTime;
    this.operationStartTimes.delete(operationId);

    const result: BenchmarkResult = {
      name: operationId,
      duration,
      timestamp: new Date(),
      metrics: metrics || {},
    };

    this.results.push(result);
    return result;
  }

  /**
   * Record sync latency sample
   */
  recordSyncLatency(latencyMs: number): void {
    if (!this.isEnabled) return;
    this.syncLatencies.push(latencyMs);
  }

  /**
   * Get sync latency statistics
   */
  getSyncLatencyStats(): SyncLatencyMetrics | null {
    if (this.syncLatencies.length === 0) {
      return null;
    }

    const sorted = [...this.syncLatencies].sort((a, b) => a - b);
    const sum = sorted.reduce((a, b) => a + b, 0);
    const avg = sum / sorted.length;

    return {
      min: sorted[0]!,
      max: sorted[sorted.length - 1]!,
      avg,
      p50: this.percentile(sorted, 50),
      p95: this.percentile(sorted, 95),
      p99: this.percentile(sorted, 99),
      samples: sorted.length,
    };
  }

  /**
   * Get memory metrics (browser)
   */
  getMemoryMetrics(): MemoryMetrics | null {
    if (typeof performance === 'undefined' || !(performance as any).memory) {
      return null;
    }

    const memory = (performance as any).memory;
    return {
      heapUsed: memory.usedJSHeapSize,
      heapTotal: memory.totalJSHeapSize,
      external: memory.jsHeapSizeLimit,
      rss: 0, // Not available in browser
    };
  }

  /**
   * Get operation statistics
   */
  getOperationStats(operationName: string): {
    count: number;
    totalDuration: number;
    avgDuration: number;
    minDuration: number;
    maxDuration: number;
  } | null {
    const operationResults = this.results.filter((r) => r.name === operationName);

    if (operationResults.length === 0) {
      return null;
    }

    const durations = operationResults.map((r) => r.duration);
    const total = durations.reduce((a, b) => a + b, 0);

    return {
      count: operationResults.length,
      totalDuration: total,
      avgDuration: total / operationResults.length,
      minDuration: Math.min(...durations),
      maxDuration: Math.max(...durations),
    };
  }

  /**
   * Get all benchmark results
   */
  getAllResults(): BenchmarkResult[] {
    return [...this.results];
  }

  /**
   * Clear all results
   */
  clear(): void {
    this.results = [];
    this.syncLatencies = [];
    this.operationStartTimes.clear();
  }

  /**
   * Export results as JSON
   */
  exportJSON(): string {
    const stats = this.getSyncLatencyStats();
    const memory = this.getMemoryMetrics();

    return JSON.stringify(
      {
        timestamp: new Date().toISOString(),
        syncLatency: stats,
        memory: memory,
        results: this.results.slice(-100), // Last 100 results
      },
      null,
      2
    );
  }

  /**
   * Calculate percentile
   */
  private percentile(sortedArray: number[], p: number): number {
    const index = Math.ceil((p / 100) * sortedArray.length) - 1;
    return sortedArray[Math.max(0, index)] ?? 0;
  }
}

/**
 * Create and configure benchmark instance
 */
export function createBenchmark(enabled: boolean = true): CollaborationBenchmark {
  return new CollaborationBenchmark(enabled);
}

/**
 * Utility function to measure function execution time
 */
export async function measureAsync<T>(
  benchmark: CollaborationBenchmark,
  operationId: string,
  fn: () => Promise<T>
): Promise<T> {
  benchmark.startOperation(operationId);
  try {
    const result = await fn();
    benchmark.endOperation(operationId);
    return result;
  } catch (error) {
    benchmark.endOperation(operationId, { error: 1 });
    throw error;
  }
}

/**
 * Utility function to measure sync function execution time
 */
export function measure<T>(
  benchmark: CollaborationBenchmark,
  operationId: string,
  fn: () => T
): T {
  benchmark.startOperation(operationId);
  try {
    const result = fn();
    benchmark.endOperation(operationId);
    return result;
  } catch (error) {
    benchmark.endOperation(operationId, { error: 1 });
    throw error;
  }
}
