import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { RedisService } from '../cache/RedisService';

export interface PerformanceMetrics {
  timestamp: string;
  cpu: {
    usage: number;
    loadAverage: number[];
  };
  memory: {
    total: number;
    used: number;
    free: number;
    usage: number;
  };
  eventLoop: {
    delay: number;
    utilization: number;
  };
  http: {
    requests: number;
    responseTime: number;
    errorRate: number;
  };
  database: {
    connections: number;
    queryTime: number;
    slowQueries: number;
  };
  redis: {
    connections: number;
    memoryUsage: number;
    hitRate: number;
  };
}

export interface OptimizationRecommendation {
  category: 'performance' | 'memory' | 'database' | 'cache' | 'security';
  severity: 'low' | 'medium' | 'high' | 'critical';
  title: string;
  description: string;
  recommendation: string;
  estimatedImpact: string;
}

@Injectable()
export class PerformanceOptimizationService {
  private readonly logger = new Logger(PerformanceOptimizationService.name);
  private metricsHistory: PerformanceMetrics[] = [];
  private readonly maxHistorySize = 1000;
  private optimizationInterval: NodeJS.Timeout;

  constructor(
    private readonly configService: ConfigService,
    private readonly redisService: RedisService,
  ) {}

  async initialize(): Promise<void> {
    this.logger.log('🚀 Initializing Performance Optimization Service...');

    try {
      await this.setupPerformanceMonitoring();
      await this.initializeOptimizations();
      await this.startOptimizationLoop();

      this.logger.log('✅ Performance Optimization Service initialized successfully');
    } catch (error) {
      this.logger.error('❌ Failed to initialize Performance Optimization Service', error);
      throw error;
    }
  }

  private async setupPerformanceMonitoring(): Promise<void> {
    this.logger.log('📊 Setting up performance monitoring...');

    const monitoringInterval = this.configService.get('PERFORMANCE_MONITORING_INTERVAL', 60000);
    
    this.optimizationInterval = setInterval(async () => {
      await this.collectPerformanceMetrics();
      await this.analyzePerformance();
      await this.applyOptimizations();
    }, monitoringInterval);

    await this.collectPerformanceMetrics();
    this.logger.log('✅ Performance monitoring setup completed');
  }

  private async collectPerformanceMetrics(): Promise<PerformanceMetrics> {
    const metrics: PerformanceMetrics = {
      timestamp: new Date().toISOString(),
      cpu: await this.collectCPUMetrics(),
      memory: await this.collectMemoryMetrics(),
      eventLoop: await this.collectEventLoopMetrics(),
      http: await this.collectHTTPMetrics(),
      database: await this.collectDatabaseMetrics(),
      redis: await this.collectRedisMetrics()
    };

    this.metricsHistory.push(metrics);
    
    if (this.metricsHistory.length > this.maxHistorySize) {
      this.metricsHistory.shift();
    }

    await this.cacheMetrics(metrics);
    return metrics;
  }

  private async collectCPUMetrics(): Promise<any> {
    const cpus = require('os').cpus();
    const cpuUsage = process.cpuUsage();
    const loadAverage = require('os').loadavg();

    return {
      usage: (cpuUsage.user + cpuUsage.system) / 1000000,
      loadAverage: loadAverage,
      cores: cpus.length
    };
  }

  private async collectMemoryMetrics(): Promise<any> {
    const totalMemory = require('os').totalmem();
    const freeMemory = require('os').freemem();
    const usedMemory = totalMemory - freeMemory;

    return {
      total: totalMemory,
      used: usedMemory,
      free: freeMemory,
      usage: (usedMemory / totalMemory) * 100
    };
  }

  private async collectEventLoopMetrics(): Promise<any> {
    return new Promise((resolve) => {
      const start = Date.now();
      setImmediate(() => {
        const delay = Date.now() - start;
        resolve({
          delay: delay,
          utilization: Math.min(delay / 10, 100)
        });
      });
    });
  }

  private async collectHTTPMetrics(): Promise<any> {
    try {
      const httpMetrics = await this.redisService.get('metrics:http');
      if (httpMetrics) {
        return JSON.parse(httpMetrics);
      }
    } catch (error) {
      this.logger.warn('Failed to collect HTTP metrics', error);
    }

    return {
      requests: 0,
      responseTime: 0,
      errorRate: 0
    };
  }

  private async collectDatabaseMetrics(): Promise<any> {
    try {
      const dbMetrics = await this.redisService.get('metrics:database');
      if (dbMetrics) {
        return JSON.parse(dbMetrics);
      }
    } catch (error) {
      this.logger.warn('Failed to collect database metrics', error);
    }

    return {
      connections: 0,
      queryTime: 0,
      slowQueries: 0
    };
  }

  private async collectRedisMetrics(): Promise<any> {
    try {
      const redisMetrics = await this.redisService.get('metrics:redis');
      if (redisMetrics) {
        return JSON.parse(redisMetrics);
      }
    } catch (error) {
      this.logger.warn('Failed to collect Redis metrics', error);
    }

    return {
      connections: 0,
      memoryUsage: 0,
      hitRate: 0
    };
  }

  private async cacheMetrics(metrics: PerformanceMetrics): Promise<void> {
    try {
      await this.redisService.set(
        'performance:metrics:latest',
        JSON.stringify(metrics),
        'EX',
        3600
      );

      await this.redisService.lpush(
        'performance:metrics:history',
        JSON.stringify(metrics)
      );

      await this.redisService.ltrim('performance:metrics:history', 0, this.maxHistorySize - 1);
    } catch (error) {
      this.logger.warn('Failed to cache metrics', error);
    }
  }

  private async analyzePerformance(): Promise<OptimizationRecommendation[]> {
    const recommendations: OptimizationRecommendation[] = [];
    const latestMetrics = this.metricsHistory[this.metricsHistory.length - 1];

    if (!latestMetrics) {
      return recommendations;
    }

    if (latestMetrics.cpu.usage > 80) {
      recommendations.push({
        category: 'performance',
        severity: 'high',
        title: 'High CPU Usage',
        description: `CPU usage is at ${latestMetrics.cpu.usage.toFixed(2)}%`,
        recommendation: 'Consider scaling horizontally or optimizing CPU-intensive operations',
        estimatedImpact: '20-30% performance improvement'
      });
    }

    if (latestMetrics.memory.usage > 85) {
      recommendations.push({
        category: 'memory',
        severity: 'critical',
        title: 'High Memory Usage',
        description: `Memory usage is at ${latestMetrics.memory.usage.toFixed(2)}%`,
        recommendation: 'Increase memory allocation or optimize memory usage',
        estimatedImpact: 'Prevent out-of-memory errors'
      });
    }

    if (latestMetrics.eventLoop.delay > 100) {
      recommendations.push({
        category: 'performance',
        severity: 'medium',
        title: 'Event Loop Delay',
        description: `Event loop delay is ${latestMetrics.eventLoop.delay}ms`,
        recommendation: 'Optimize synchronous operations and use async/await properly',
        estimatedImpact: '15-25% performance improvement'
      });
    }

    if (latestMetrics.http.responseTime > 1000) {
      recommendations.push({
        category: 'performance',
        severity: 'medium',
        title: 'Slow HTTP Response Time',
        description: `Average response time is ${latestMetrics.http.responseTime}ms`,
        recommendation: 'Implement caching, optimize database queries, and use CDN',
        estimatedImpact: '30-50% response time reduction'
      });
    }

    if (latestMetrics.http.errorRate > 0.05) {
      recommendations.push({
        category: 'performance',
        severity: 'high',
        title: 'High Error Rate',
        description: `Error rate is ${(latestMetrics.http.errorRate * 100).toFixed(2)}%`,
        recommendation: 'Investigate and fix error sources, implement retry logic',
        estimatedImpact: 'Improve reliability and user experience'
      });
    }

    if (latestMetrics.database.slowQueries > 10) {
      recommendations.push({
        category: 'database',
        severity: 'high',
        title: 'Many Slow Queries',
        description: `${latestMetrics.database.slowQueries} slow queries detected`,
        recommendation: 'Optimize database queries, add indexes, and use query caching',
        estimatedImpact: '40-60% query performance improvement'
      });
    }

    if (latestMetrics.redis.hitRate < 0.8) {
      recommendations.push({
        category: 'cache',
        severity: 'medium',
        title: 'Low Cache Hit Rate',
        description: `Cache hit rate is ${(latestMetrics.redis.hitRate * 100).toFixed(2)}%`,
        recommendation: 'Optimize cache strategy and increase cache size',
        estimatedImpact: '20-30% performance improvement'
      });
    }

    if (recommendations.length > 0) {
      await this.cacheRecommendations(recommendations);
    }

    return recommendations;
  }

  private async cacheRecommendations(recommendations: OptimizationRecommendation[]): Promise<void> {
    try {
      await this.redisService.set(
        'performance:recommendations',
        JSON.stringify(recommendations),
        'EX',
        3600
      );

      this.logger.warn(`⚠️  ${recommendations.length} performance recommendations generated`);
    } catch (error) {
      this.logger.warn('Failed to cache recommendations', error);
    }
  }

  private async applyOptimizations(): Promise<void> {
    const latestMetrics = this.metricsHistory[this.metricsHistory.length - 1];
    
    if (!latestMetrics) {
      return;
    }

    if (latestMetrics.memory.usage > 85) {
      await this.optimizeMemory();
    }

    if (latestMetrics.redis.hitRate < 0.8) {
      await this.optimizeCache();
    }

    if (latestMetrics.database.slowQueries > 10) {
      await this.optimizeDatabase();
    }

    if (latestMetrics.http.responseTime > 1000) {
      await this.optimizeHTTP();
    }
  }

  private async optimizeMemory(): Promise<void> {
    this.logger.log('🧹 Optimizing memory usage...');

    try {
      if (global.gc) {
        global.gc();
        this.logger.log('✅ Garbage collection triggered');
      }

      this.metricsHistory = this.metricsHistory.slice(-100);

      await this.redisService.del('performance:metrics:history');

      this.logger.log('✅ Memory optimization completed');
    } catch (error) {
      this.logger.warn('Memory optimization failed', error);
    }
  }

  private async optimizeCache(): Promise<void> {
    this.logger.log('🔄 Optimizing cache...');

    try {
      const cacheKeys = await this.redisService.keys('cache:*');
      
      for (const key of cacheKeys) {
        const ttl = await this.redisService.ttl(key);
        if (ttl === -1) {
          await this.redisService.expire(key, 3600);
        }
      }

      this.logger.log(`✅ Cache optimization completed: ${cacheKeys.length} keys processed`);
    } catch (error) {
      this.logger.warn('Cache optimization failed', error);
    }
  }

  private async optimizeDatabase(): Promise<void> {
    this.logger.log('🗄️  Optimizing database...');

    try {
      await this.redisService.set('database:optimize:triggered', new Date().toISOString());
      
      this.logger.log('✅ Database optimization triggered');
    } catch (error) {
      this.logger.warn('Database optimization failed', error);
    }
  }

  private async optimizeHTTP(): Promise<void> {
    this.logger.log('🌐 Optimizing HTTP performance...');

    try {
      await this.redisService.set('http:optimize:triggered', new Date().toISOString());
      
      this.logger.log('✅ HTTP optimization triggered');
    } catch (error) {
      this.logger.warn('HTTP optimization failed', error);
    }
  }

  private async initializeOptimizations(): Promise<void> {
    this.logger.log('⚙️  Initializing optimizations...');

    await this.configureNodeMemory();
    await this.configureDatabaseConnectionPool();
    await this.configureRedisConnection();
    await this.configureHTTPCompression();
    await this.configureResponseCaching();

    this.logger.log('✅ Optimizations initialized');
  }

  private async configureNodeMemory(): Promise<void> {
    const maxOldSpaceSize = this.configService.get('NODE_MAX_OLD_SPACE_SIZE', '4096');
    process.env.NODE_OPTIONS = `--max-old-space-size=${maxOldSpaceSize}`;

    this.logger.log(`✅ Node.js memory limit set to ${maxOldSpaceSize}MB`);
  }

  private async configureDatabaseConnectionPool(): Promise<void> {
    const poolSize = this.configService.get('DB_POOL_SIZE', 10);
    const connectionTimeout = this.configService.get('DB_CONNECTION_TIMEOUT', 30000);

    await this.redisService.set('database:pool:size', poolSize.toString());
    await this.redisService.set('database:pool:timeout', connectionTimeout.toString());

    this.logger.log(`✅ Database connection pool configured: ${poolSize} connections`);
  }

  private async configureRedisConnection(): Promise<void> {
    const maxRetries = this.configService.get('REDIS_MAX_RETRIES', 3);
    const retryDelay = this.configService.get('REDIS_RETRY_DELAY', 1000);

    await this.redisService.set('redis:config:maxRetries', maxRetries.toString());
    await this.redisService.set('redis:config:retryDelay', retryDelay.toString());

    this.logger.log(`✅ Redis connection configured: ${maxRetries} retries`);
  }

  private async configureHTTPCompression(): Promise<void> {
    const compressionLevel = this.configService.get('HTTP_COMPRESSION_LEVEL', 6);
    const threshold = this.configService.get('HTTP_COMPRESSION_THRESHOLD', 1024);

    await this.redisService.set('http:compression:level', compressionLevel.toString());
    await this.redisService.set('http:compression:threshold', threshold.toString());

    this.logger.log(`✅ HTTP compression configured: level ${compressionLevel}`);
  }

  private async configureResponseCaching(): Promise<void> {
    const cacheTTL = this.configService.get('CACHE_TTL', 3600);
    const cacheSize = this.configService.get('CACHE_SIZE', 1000);

    await this.redisService.set('cache:config:ttl', cacheTTL.toString());
    await this.redisService.set('cache:config:size', cacheSize.toString());

    this.logger.log(`✅ Response caching configured: ${cacheTTL}s TTL`);
  }

  private async startOptimizationLoop(): Promise<void> {
    this.logger.log('🔄 Starting optimization loop...');

    const optimizationInterval = this.configService.get('OPTIMIZATION_INTERVAL', 300000);
    
    setInterval(async () => {
      await this.collectPerformanceMetrics();
      await this.analyzePerformance();
      await this.applyOptimizations();
    }, optimizationInterval);

    this.logger.log(`✅ Optimization loop started: ${optimizationInterval}ms interval`);
  }

  async getPerformanceMetrics(): Promise<PerformanceMetrics[]> {
    return this.metricsHistory;
  }

  async getLatestMetrics(): Promise<PerformanceMetrics | null> {
    return this.metricsHistory[this.metricsHistory.length - 1] || null;
  }

  async getRecommendations(): Promise<OptimizationRecommendation[]> {
    try {
      const recommendations = await this.redisService.get('performance:recommendations');
      return recommendations ? JSON.parse(recommendations) : [];
    } catch (error) {
      this.logger.warn('Failed to get recommendations', error);
      return [];
    }
  }

  async generatePerformanceReport(): Promise<any> {
    const latestMetrics = await this.getLatestMetrics();
    const recommendations = await this.getRecommendations();

    return {
      timestamp: new Date().toISOString(),
      metrics: latestMetrics,
      recommendations: recommendations,
      summary: {
        cpuUsage: latestMetrics?.cpu.usage || 0,
        memoryUsage: latestMetrics?.memory.usage || 0,
        eventLoopDelay: latestMetrics?.eventLoop.delay || 0,
        responseTime: latestMetrics?.http.responseTime || 0,
        errorRate: latestMetrics?.http.errorRate || 0,
        cacheHitRate: latestMetrics?.redis.hitRate || 0
      },
      health: {
        status: this.calculateHealthStatus(latestMetrics),
        score: this.calculateHealthScore(latestMetrics)
      }
    };
  }

  private calculateHealthStatus(metrics: PerformanceMetrics | null): string {
    if (!metrics) return 'unknown';

    const issues = [
      metrics.cpu.usage > 80,
      metrics.memory.usage > 85,
      metrics.eventLoop.delay > 100,
      metrics.http.responseTime > 1000,
      metrics.http.errorRate > 0.05
    ];

    const criticalIssues = issues.filter(Boolean).length;

    if (criticalIssues === 0) return 'healthy';
    if (criticalIssues <= 2) return 'degraded';
    return 'unhealthy';
  }

  private calculateHealthScore(metrics: PerformanceMetrics | null): number {
    if (!metrics) return 0;

    let score = 100;

    score -= Math.max(0, metrics.cpu.usage - 50) * 0.5;
    score -= Math.max(0, metrics.memory.usage - 70) * 0.5;
    score -= Math.max(0, metrics.eventLoop.delay - 50) * 0.3;
    score -= Math.max(0, metrics.http.responseTime - 500) * 0.02;
    score -= metrics.http.errorRate * 100;

    return Math.max(0, Math.min(100, score));
  }

  async shutdown(): Promise<void> {
    this.logger.log('🛑 Shutting down Performance Optimization Service...');

    if (this.optimizationInterval) {
      clearInterval(this.optimizationInterval);
    }

    await this.redisService.del('performance:metrics:latest');
    await this.redisService.del('performance:recommendations');

    this.logger.log('✅ Performance Optimization Service shutdown completed');
  }
}