import { EventEmitter } from 'events';

export interface GamePerformanceMetrics {
  gameId: string;
  timestamp: Date;
  activePlayers: number;
  totalPlays: number;
  totalWagers: number;
  totalPayouts: number;
  averageBet: number;
  averageWin: number;
  winRate: number;
  sessionCount: number;
  averageSessionDuration: number;
  playerRetention: number;
  engagementScore: number;
  revenuePerPlayer: number;
}

export interface GameHealthStatus {
  gameId: string;
  status: 'healthy' | 'warning' | 'critical' | 'offline';
  lastUpdate: Date;
  uptime: number;
  errorRate: number;
  avgResponseTime: number;
  issues: string[];
}

export interface PerformanceAlert {
  id: string;
  gameId: string;
  alertType: 'low_engagement' | 'high_error_rate' | 'revenue_drop' | 'player_drop' | 'performance_issue';
  severity: 'info' | 'warning' | 'critical';
  message: string;
  threshold: number;
  currentValue: number;
  createdAt: Date;
  resolvedAt?: Date;
}

export interface GameEngagementMetrics {
  gameId: string;
  hourlyPlays: number[];
  dailyPlays: number[];
  weeklyTrend: number;
  monthlyTrend: number;
  peakHour: number;
  peakDay: string;
  averagePlayersPerHour: number;
  churnRate: number;
  returnRate: number;
}

class GamePerformanceMonitoringService extends EventEmitter {
  private performanceMetrics: Map<string, GamePerformanceMetrics[]> = new Map();
  private healthStatus: Map<string, GameHealthStatus> = new Map();
  private alerts: Map<string, PerformanceAlert[]> = new Map();
  private engagementMetrics: Map<string, GameEngagementMetrics> = new Map();
  private activeConnections: Map<string, Set<string>> = new Map();

  constructor() {
    super();
  }

  /**
   * Record game metrics
   */
  recordGameMetrics(
    gameId: string,
    metrics: {
      activePlayers: number;
      totalPlays: number;
      totalWagers: number;
      totalPayouts: number;
      sessionCount: number;
      averageSessionDuration: number;
    }
  ): GamePerformanceMetrics {
    const performanceMetric: GamePerformanceMetrics = {
      gameId,
      timestamp: new Date(),
      activePlayers: metrics.activePlayers,
      totalPlays: metrics.totalPlays,
      totalWagers: metrics.totalWagers,
      totalPayouts: metrics.totalPayouts,
      averageBet: metrics.totalWagers > 0 ? metrics.totalWagers / metrics.totalPlays : 0,
      averageWin: metrics.totalPayouts > 0 ? metrics.totalPayouts / metrics.totalPlays : 0,
      winRate: metrics.totalWagers > 0 ? (metrics.totalPayouts / metrics.totalWagers) * 100 : 0,
      sessionCount: metrics.sessionCount,
      averageSessionDuration: metrics.averageSessionDuration,
      playerRetention: 0,
      engagementScore: this.calculateEngagementScore(metrics),
      revenuePerPlayer: metrics.activePlayers > 0 ? (metrics.totalWagers - metrics.totalPayouts) / metrics.activePlayers : 0,
    };

    const metricsArray = this.performanceMetrics.get(gameId) || [];
    metricsArray.push(performanceMetric);

    // Keep only last 100 records per game
    if (metricsArray.length > 100) {
      metricsArray.shift();
    }

    this.performanceMetrics.set(gameId, metricsArray);
    this.emit('metrics_recorded', performanceMetric);

    return performanceMetric;
  }

  /**
   * Update game health status
   */
  updateGameHealthStatus(
    gameId: string,
    status: 'healthy' | 'warning' | 'critical' | 'offline',
    errorRate: number,
    avgResponseTime: number,
    issues: string[] = []
  ): GameHealthStatus {
    const healthStatus: GameHealthStatus = {
      gameId,
      status,
      lastUpdate: new Date(),
      uptime: 99.9,
      errorRate,
      avgResponseTime,
      issues,
    };

    this.healthStatus.set(gameId, healthStatus);
    this.emit('health_status_updated', healthStatus);

    // Check if we need to create alerts
    if (status === 'critical' || errorRate > 5) {
      this.createAlert(gameId, 'performance_issue', 'critical', `Game health is ${status}`, 5, errorRate);
    }

    return healthStatus;
  }

  /**
   * Get game health status
   */
  getGameHealthStatus(gameId: string): GameHealthStatus | null {
    return this.healthStatus.get(gameId) || null;
  }

  /**
   * Create performance alert
   */
  createAlert(
    gameId: string,
    alertType: 'low_engagement' | 'high_error_rate' | 'revenue_drop' | 'player_drop' | 'performance_issue',
    severity: 'info' | 'warning' | 'critical',
    message: string,
    threshold: number,
    currentValue: number
  ): PerformanceAlert {
    const alert: PerformanceAlert = {
      id: `alert-${Date.now()}`,
      gameId,
      alertType,
      severity,
      message,
      threshold,
      currentValue,
      createdAt: new Date(),
    };

    const gameAlerts = this.alerts.get(gameId) || [];
    gameAlerts.push(alert);
    this.alerts.set(gameId, gameAlerts);

    this.emit('alert_created', alert);

    return alert;
  }

  /**
   * Get active alerts for game
   */
  getGameAlerts(gameId: string): PerformanceAlert[] {
    return (this.alerts.get(gameId) || []).filter((a) => !a.resolvedAt);
  }

  /**
   * Resolve alert
   */
  resolveAlert(alertId: string): PerformanceAlert | null {
    for (const gameAlerts of this.alerts.values()) {
      const alert = gameAlerts.find((a) => a.id === alertId);
      if (alert) {
        alert.resolvedAt = new Date();
        this.emit('alert_resolved', alert);
        return alert;
      }
    }
    return null;
  }

  /**
   * Get latest metrics for game
   */
  getLatestMetrics(gameId: string): GamePerformanceMetrics | null {
    const metricsArray = this.performanceMetrics.get(gameId);
    if (!metricsArray || metricsArray.length === 0) {
      return null;
    }
    return metricsArray[metricsArray.length - 1];
  }

  /**
   * Get metrics history
   */
  getMetricsHistory(gameId: string, limit: number = 50): GamePerformanceMetrics[] {
    const metricsArray = this.performanceMetrics.get(gameId) || [];
    return metricsArray.slice(-limit);
  }

  /**
   * Calculate engagement score
   */
  private calculateEngagementScore(metrics: {
    activePlayers: number;
    totalPlays: number;
    sessionCount: number;
    averageSessionDuration: number;
  }): number {
    let score = 0;

    // Active players factor (0-30 points)
    score += Math.min(30, metrics.activePlayers / 10);

    // Play frequency factor (0-30 points)
    score += Math.min(30, metrics.totalPlays / 100);

    // Session count factor (0-20 points)
    score += Math.min(20, metrics.sessionCount / 50);

    // Session duration factor (0-20 points)
    score += Math.min(20, metrics.averageSessionDuration / 60);

    return Math.round(score);
  }

  /**
   * Track player engagement
   */
  trackPlayerEngagement(gameId: string, playerId: string, engagementType: 'play' | 'win' | 'share' | 'return'): void {
    const engagementMetrics = this.engagementMetrics.get(gameId) || {
      gameId,
      hourlyPlays: Array(24).fill(0),
      dailyPlays: Array(7).fill(0),
      weeklyTrend: 0,
      monthlyTrend: 0,
      peakHour: 0,
      peakDay: 'Monday',
      averagePlayersPerHour: 0,
      churnRate: 0,
      returnRate: 0,
    };

    const now = new Date();
    const hour = now.getHours();
    const day = now.getDay();

    if (engagementType === 'play') {
      engagementMetrics.hourlyPlays[hour]++;
      engagementMetrics.dailyPlays[day]++;
    }

    // Calculate peak hour
    engagementMetrics.peakHour = engagementMetrics.hourlyPlays.indexOf(Math.max(...engagementMetrics.hourlyPlays));

    // Calculate peak day
    const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    const peakDayIndex = engagementMetrics.dailyPlays.indexOf(Math.max(...engagementMetrics.dailyPlays));
    engagementMetrics.peakDay = days[peakDayIndex];

    this.engagementMetrics.set(gameId, engagementMetrics);
    this.emit('engagement_tracked', { gameId, playerId, engagementType });
  }

  /**
   * Get engagement metrics
   */
  getEngagementMetrics(gameId: string): GameEngagementMetrics | null {
    return this.engagementMetrics.get(gameId) || null;
  }

  /**
   * Register WebSocket connection
   */
  registerConnection(gameId: string, connectionId: string): void {
    const connections = this.activeConnections.get(gameId) || new Set();
    connections.add(connectionId);
    this.activeConnections.set(gameId, connections);

    this.emit('connection_registered', { gameId, connectionId });
  }

  /**
   * Unregister WebSocket connection
   */
  unregisterConnection(gameId: string, connectionId: string): void {
    const connections = this.activeConnections.get(gameId);
    if (connections) {
      connections.delete(connectionId);
      if (connections.size === 0) {
        this.activeConnections.delete(gameId);
      }
    }

    this.emit('connection_unregistered', { gameId, connectionId });
  }

  /**
   * Get active connections count
   */
  getActiveConnectionsCount(gameId: string): number {
    return this.activeConnections.get(gameId)?.size || 0;
  }

  /**
   * Get all active games
   */
  getAllActiveGames(): string[] {
    return Array.from(this.activeConnections.keys());
  }

  /**
   * Get performance comparison
   */
  getPerformanceComparison(gameIds: string[]): Record<string, GamePerformanceMetrics | null> {
    const comparison: Record<string, GamePerformanceMetrics | null> = {};
    for (const gameId of gameIds) {
      comparison[gameId] = this.getLatestMetrics(gameId);
    }
    return comparison;
  }

  /**
   * Get performance trends
   */
  getPerformanceTrends(gameId: string, timeWindow: number = 24): {
    plays: number[];
    wagers: number[];
    payouts: number[];
    activePlayers: number[];
  } {
    const metricsArray = this.performanceMetrics.get(gameId) || [];
    const recentMetrics = metricsArray.slice(-timeWindow);

    return {
      plays: recentMetrics.map((m) => m.totalPlays),
      wagers: recentMetrics.map((m) => m.totalWagers),
      payouts: recentMetrics.map((m) => m.totalPayouts),
      activePlayers: recentMetrics.map((m) => m.activePlayers),
    };
  }

  /**
   * Get all health statuses
   */
  getAllHealthStatuses(): GameHealthStatus[] {
    return Array.from(this.healthStatus.values());
  }

  /**
   * Get critical games
   */
  getCriticalGames(): GameHealthStatus[] {
    return Array.from(this.healthStatus.values()).filter((h) => h.status === 'critical');
  }

  /**
   * Get performance summary
   */
  getPerformanceSummary(): {
    totalGames: number;
    healthyGames: number;
    warningGames: number;
    criticalGames: number;
    totalActivePlayers: number;
    totalWagers: number;
    totalPayouts: number;
  } {
    const healthStatuses = Array.from(this.healthStatus.values());
    const metrics = Array.from(this.performanceMetrics.values()).map((arr) => arr[arr.length - 1]);

    const healthyGames = healthStatuses.filter((h) => h.status === 'healthy').length;
    const warningGames = healthStatuses.filter((h) => h.status === 'warning').length;
    const criticalGames = healthStatuses.filter((h) => h.status === 'critical').length;

    const totalActivePlayers = metrics.reduce((sum, m) => sum + (m?.activePlayers || 0), 0);
    const totalWagers = metrics.reduce((sum, m) => sum + (m?.totalWagers || 0), 0);
    const totalPayouts = metrics.reduce((sum, m) => sum + (m?.totalPayouts || 0), 0);

    return {
      totalGames: healthStatuses.length,
      healthyGames,
      warningGames,
      criticalGames,
      totalActivePlayers,
      totalWagers,
      totalPayouts,
    };
  }
}

// Export singleton instance
export const gamePerformanceMonitoringService = new GamePerformanceMonitoringService();

export default GamePerformanceMonitoringService;
