/**
 * Performance Optimizer Service
 * Implements performance optimizations for large datasets and caching strategies
 */

import { TimeEntry } from '@/lib/types/database';
import { AnalyticsInsight, AggregateAnalysis } from '@/lib/types/analytics';

export interface CacheConfig {
  ttl: number; // Time to live in milliseconds
  maxSize: number; // Maximum number of items in cache
  strategy: 'lru' | 'fifo' | 'lfu';
}

export interface PerformanceMetrics {
  queryTime: number;
  cacheHitRate: number;
  memoryUsage: number;
  recordsProcessed: number;
  recordsPerSecond: number;
}

export class PerformanceOptimizer {
  private cache: Map<string, { data: any; timestamp: number; accessCount: number }> = new Map();
  private cacheConfig: CacheConfig = {
    ttl: 5 * 60 * 1000, // 5 minutes default
    maxSize: 1000,
    strategy: 'lru',
  };

  constructor(config?: Partial<CacheConfig>) {
    if (config) {
      this.cacheConfig = { ...this.cacheConfig, ...config };
    }
  }

  /**
   * Get cached data
   */
  getCachedData(key: string): any | null {
    const item = this.cache.get(key);
    
    if (!item) {
      return null;
    }

    // Check if item is expired
    if (Date.now() - item.timestamp > this.cacheConfig.ttl) {
      this.cache.delete(key);
      return null;
    }

    // Update access count for LFU strategy
    item.accessCount++;
    
    return item.data;
  }

  /**
   * Set cached data
   */
  setCachedData(key: string, data: any): void {
    // Remove oldest items if cache is full
    if (this.cache.size >= this.cacheConfig.maxSize) {
      this.evictCache();
    }

    this.cache.set(key, {
      data,
      timestamp: Date.now(),
      accessCount: 1,
    });
  }

  /**
   * Clear cache
   */
  clearCache(): void {
    this.cache.clear();
  }

  /**
   * Get cache statistics
   */
  getCacheStats(): {
    size: number;
    maxSize: number;
    hitRate: number;
    memoryUsage: number;
  } {
    return {
      size: this.cache.size,
      maxSize: this.cacheConfig.maxSize,
      hitRate: 0, // Would need to track hits/misses for real implementation
      memoryUsage: this.estimateMemoryUsage(),
    };
  }

  /**
   * Optimize time entries query with pagination and filtering
   */
  optimizeTimeEntriesQuery(
    baseQuery: string,
    filters: Record<string, any>,
    pagination: { limit: number; offset: number }
  ): { query: string; params: any[] } {
    const conditions: string[] = [];
    const params: any[] = [];
    let paramIndex = 1;

    // Add filter conditions
    Object.entries(filters).forEach(([key, value]) => {
      if (value !== undefined && value !== null) {
        if (Array.isArray(value)) {
          conditions.push(`${key} = ANY($${paramIndex})`);
          params.push(value);
        } else {
          conditions.push(`${key} = $${paramIndex}`);
          params.push(value);
        }
        paramIndex++;
      }
    });

    // Build WHERE clause
    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

    // Add pagination
    const query = `
      ${baseQuery}
      ${whereClause}
      ORDER BY entry_date DESC
      LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
    `;

    params.push(pagination.limit, pagination.offset);

    return { query, params };
  }

  /**
   * Batch process large datasets
   */
  async batchProcess<T, R>(
    items: T[],
    processor: (batch: T[]) => Promise<R[]>,
    batchSize: number = 100,
    onProgress?: (processed: number, total: number) => void
  ): Promise<R[]> {
    const results: R[] = [];
    
    for (let i = 0; i < items.length; i += batchSize) {
      const batch = items.slice(i, i + batchSize);
      const batchResults = await processor(batch);
      results.push(...batchResults);
      
      if (onProgress) {
        onProgress(Math.min(i + batchSize, items.length), items.length);
      }
      
      // Small delay to prevent overwhelming the system
      await new Promise(resolve => setTimeout(resolve, 10));
    }
    
    return results;
  }

  /**
   * Optimize analytics calculations for large datasets
   */
  optimizeAnalyticsCalculation(timeEntries: TimeEntry[]): {
    summary: {
      totalEntries: number;
      totalHours: number;
      averageHoursPerEntry: number;
      billableEntries: number;
      approvedEntries: number;
    };
    scores: {
      activity: number;
      content: number;
      timeliness: number;
      overall: number;
    };
  } {
    // Use efficient single-pass calculations
    let totalHours = 0;
    let billableEntries = 0;
    let approvedEntries = 0;
    let activityScoreSum = 0;
    let contentScoreSum = 0;
    let timelinessScoreSum = 0;

    for (const entry of timeEntries) {
      totalHours += entry.hours_worked;
      
      if (entry.billable) billableEntries++;
      if (entry.approved) approvedEntries++;
      
      // Simplified scoring for performance (would use full analytics engine in real implementation)
      activityScoreSum += this.calculateQuickActivityScore(entry);
      contentScoreSum += this.calculateQuickContentScore(entry);
      timelinessScoreSum += this.calculateQuickTimelinessScore(entry);
    }

    const count = timeEntries.length;
    
    return {
      summary: {
        totalEntries: count,
        totalHours,
        averageHoursPerEntry: count > 0 ? totalHours / count : 0,
        billableEntries,
        approvedEntries,
      },
      scores: {
        activity: count > 0 ? activityScoreSum / count : 0,
        content: count > 0 ? contentScoreSum / count : 0,
        timeliness: count > 0 ? timelinessScoreSum / count : 0,
        overall: count > 0 ? (activityScoreSum + contentScoreSum + timelinessScoreSum) / (3 * count) : 0,
      },
    };
  }

  /**
   * Generate performance metrics
   */
  generateMetrics(startTime: number, recordsProcessed: number): PerformanceMetrics {
    const queryTime = Date.now() - startTime;
    
    return {
      queryTime,
      cacheHitRate: this.getCacheStats().hitRate,
      memoryUsage: this.getCacheStats().memoryUsage,
      recordsProcessed,
      recordsPerSecond: recordsProcessed > 0 ? (recordsProcessed / queryTime) * 1000 : 0,
    };
  }

  /**
   * Optimize insight generation by grouping and batching
   */
  optimizeInsightGeneration(
    timeEntries: TimeEntry[],
    existingInsights: AnalyticsInsight[] = []
  ): AnalyticsInsight[] {
    const insights: AnalyticsInsight[] = [...existingInsights];
    
    // Group by resource for efficiency
    const resourceGroups = new Map<number, TimeEntry[]>();
    
    for (const entry of timeEntries) {
      if (!resourceGroups.has(entry.resource_id)) {
        resourceGroups.set(entry.resource_id, []);
      }
      resourceGroups.get(entry.resource_id)!.push(entry);
    }
    
    // Generate insights per resource
    for (const [resourceId, entries] of resourceGroups) {
      const totalHours = entries.reduce((sum, entry) => sum + entry.hours_worked, 0);
      const avgScore = entries.reduce((sum, entry) => 
        sum + this.calculateQuickOverallScore(entry), 0) / entries.length;
      
      // Add insight if needed
      if (avgScore < 0.5) {
        insights.push({
          type: 'warning',
          category: 'activity',
          title: `Low Performance: Resource ${resourceId}`,
          description: `Average score: ${(avgScore * 100).toFixed(1)}%`,
          recommendation: 'Review time entry quality and provide training',
          severity: 'medium',
          actionable: true,
        });
      }
      
      if (totalHours > 40) { // More than 40 hours in period
        insights.push({
          type: 'info',
          category: 'performance',
          title: `High Activity: Resource ${resourceId}`,
          description: `${Number(totalHours).toFixed(1)} hours logged`,
          recommendation: 'Monitor workload and resource allocation',
          severity: 'low',
          actionable: true,
        });
      }
    }
    
    return insights;
  }

  /**
   * Memory-efficient data streaming for large exports
   */
  async* streamDataForExport<T>(
    data: T[],
    chunkSize: number = 1000
  ): AsyncGenerator<T[], void, unknown> {
    for (let i = 0; i < data.length; i += chunkSize) {
      yield data.slice(i, i + chunkSize);
      
      // Allow event loop to process other tasks
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }

  /**
   * Private helper methods
   */
  private evictCache(): void {
    switch (this.cacheConfig.strategy) {
      case 'lru':
        this.evictLRU();
        break;
      case 'fifo':
        this.evictFIFO();
        break;
      case 'lfu':
        this.evictLFU();
        break;
    }
  }

  private evictLRU(): void {
    let oldestKey = '';
    let oldestTime = Date.now();
    
    for (const [key, item] of this.cache.entries()) {
      if (item.timestamp < oldestTime) {
        oldestTime = item.timestamp;
        oldestKey = key;
      }
    }
    
    if (oldestKey) {
      this.cache.delete(oldestKey);
    }
  }

  private evictFIFO(): void {
    const firstKey = this.cache.keys().next().value;
    if (firstKey) {
      this.cache.delete(firstKey);
    }
  }

  private evictLFU(): void {
    let leastUsedKey = '';
    let leastCount = Infinity;
    
    for (const [key, item] of this.cache.entries()) {
      if (item.accessCount < leastCount) {
        leastCount = item.accessCount;
        leastUsedKey = key;
      }
    }
    
    if (leastUsedKey) {
      this.cache.delete(leastUsedKey);
    }
  }

  private estimateMemoryUsage(): number {
    // Rough estimation - in real implementation would use more sophisticated tracking
    let totalSize = 0;
    
    for (const [key, item] of this.cache.entries()) {
      totalSize += key.length * 2; // String size
      totalSize += JSON.stringify(item.data).length * 2; // Data size
      totalSize += 16; // Metadata overhead
    }
    
    return totalSize;
  }

  private calculateQuickActivityScore(entry: TimeEntry): number {
    let score = 0;
    if (entry.title) score += 0.25;
    if (entry.notes && entry.notes.length > 10) score += 0.25;
    if (entry.start_date_time && entry.end_date_time) score += 0.25;
    if (entry.ticket_id || entry.task_id) score += 0.25;
    return score;
  }

  private calculateQuickContentScore(entry: TimeEntry): number {
    let score = 0;
    if (entry.title && entry.title.length > 5) score += 0.3;
    if (entry.notes && entry.notes.length > 20) score += 0.4;
    if (entry.internal_notes) score += 0.3;
    return score;
  }

  private calculateQuickTimelinessScore(entry: TimeEntry): number {
    const entryDate = new Date(entry.entry_date);
    const createdDate = new Date(entry.created_at);
    const delayDays = (createdDate.getTime() - entryDate.getTime()) / (1000 * 60 * 60 * 24);
    
    if (delayDays <= 1) return 1.0;
    if (delayDays <= 3) return 0.8;
    if (delayDays <= 7) return 0.6;
    return 0.4;
  }

  private calculateQuickOverallScore(entry: TimeEntry): number {
    return (
      this.calculateQuickActivityScore(entry) * 0.3 +
      this.calculateQuickContentScore(entry) * 0.4 +
      this.calculateQuickTimelinessScore(entry) * 0.3
    );
  }
}

// Create singleton instance with optimized configuration
export const performanceOptimizer = new PerformanceOptimizer({
  ttl: 10 * 60 * 1000, // 10 minutes
  maxSize: 500,
  strategy: 'lru',
});
