import { EventEmitter } from 'events';

export type GameEventType = 'spin-completed' | 'win-detected' | 'score-updated' | 'level-gained' | 'streak-updated';

export interface GameEvent {
  userId: string;
  gameId: string;
  eventType: GameEventType;
  timestamp: Date;
  data: {
    spinId?: string;
    winAmount?: number;
    scoreGained?: number;
    newScore?: number;
    previousScore?: number;
    newLevel?: number;
    previousLevel?: number;
    newStreak?: number;
    previousStreak?: number;
    betAmount?: number;
    multiplier?: number;
    lineWins?: number;
    bonusTriggered?: boolean;
    metadata?: Record<string, any>;
  };
}

export interface GameSession {
  userId: string;
  gameId: string;
  sessionId: string;
  startTime: Date;
  endTime?: Date;
  totalSpins: number;
  totalWins: number;
  totalScore: number;
  totalBet: number;
  netWinnings: number;
  sessionData: Record<string, any>;
}

class GameEventSystem extends EventEmitter {
  private events: GameEvent[] = [];
  private sessions: Map<string, GameSession> = new Map();
  private userStats: Map<string, { totalSpins: number; totalWins: number; totalScore: number }> = new Map();
  private eventListeners: Map<string, Set<(event: GameEvent) => void>> = new Map();

  constructor() {
    super();
    this.initializeEventTypes();
  }

  /**
   * Initialize event type tracking
   */
  private initializeEventTypes(): void {
    const eventTypes: GameEventType[] = ['spin-completed', 'win-detected', 'score-updated', 'level-gained', 'streak-updated'];
    for (const type of eventTypes) {
      this.eventListeners.set(type, new Set());
    }
  }

  /**
   * Record a game event
   */
  recordEvent(event: GameEvent): void {
    this.events.push({
      ...event,
      timestamp: new Date(),
    });

    // Update user stats
    const stats = this.userStats.get(event.userId) || { totalSpins: 0, totalWins: 0, totalScore: 0 };

    if (event.eventType === 'spin-completed') {
      stats.totalSpins++;
    }
    if (event.eventType === 'win-detected' && event.data.winAmount) {
      stats.totalWins++;
    }
    if (event.eventType === 'score-updated' && event.data.newScore) {
      stats.totalScore = event.data.newScore;
    }

    this.userStats.set(event.userId, stats);

    // Emit event
    this.emit('game-event', event);
    this.emit(`event:${event.eventType}`, event);

    // Call registered listeners
    const listeners = this.eventListeners.get(event.eventType);
    if (listeners) {
      for (const listener of listeners) {
        try {
          listener(event);
        } catch (err) {
          console.error(`Error in event listener for ${event.eventType}:`, err);
        }
      }
    }
  }

  /**
   * Register a listener for specific event type
   */
  onGameEvent(eventType: GameEventType, callback: (event: GameEvent) => void): () => void {
    const listeners = this.eventListeners.get(eventType);
    if (listeners) {
      listeners.add(callback);
    }

    // Return unsubscribe function
    return () => {
      if (listeners) {
        listeners.delete(callback);
      }
    };
  }

  /**
   * Start a game session
   */
  startSession(userId: string, gameId: string): GameSession {
    const sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;

    const session: GameSession = {
      userId,
      gameId,
      sessionId,
      startTime: new Date(),
      totalSpins: 0,
      totalWins: 0,
      totalScore: 0,
      totalBet: 0,
      netWinnings: 0,
      sessionData: {},
    };

    this.sessions.set(sessionId, session);

    this.emit('session-started', { userId, gameId, sessionId, timestamp: new Date() });

    return session;
  }

  /**
   * End a game session
   */
  endSession(sessionId: string): GameSession | null {
    const session = this.sessions.get(sessionId);
    if (!session) return null;

    session.endTime = new Date();

    this.emit('session-ended', { sessionId, userId: session.userId, timestamp: new Date() });

    return session;
  }

  /**
   * Update session data
   */
  updateSessionData(sessionId: string, data: Record<string, any>): void {
    const session = this.sessions.get(sessionId);
    if (session) {
      session.sessionData = { ...session.sessionData, ...data };
    }
  }

  /**
   * Get user's game statistics
   */
  getUserStats(userId: string): { totalSpins: number; totalWins: number; totalScore: number } {
    return this.userStats.get(userId) || { totalSpins: 0, totalWins: 0, totalScore: 0 };
  }

  /**
   * Get recent events for user
   */
  getRecentEvents(userId: string, limit: number = 50): GameEvent[] {
    return this.events
      .filter(e => e.userId === userId)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
      .slice(0, limit);
  }

  /**
   * Get events by type
   */
  getEventsByType(userId: string, eventType: GameEventType): GameEvent[] {
    return this.events.filter(e => e.userId === userId && e.eventType === eventType);
  }

  /**
   * Get game-specific events
   */
  getGameEvents(userId: string, gameId: string, limit: number = 50): GameEvent[] {
    return this.events
      .filter(e => e.userId === userId && e.gameId === gameId)
      .sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
      .slice(0, limit);
  }

  /**
   * Get session details
   */
  getSession(sessionId: string): GameSession | null {
    return this.sessions.get(sessionId) || null;
  }

  /**
   * Get user's recent sessions
   */
  getUserSessions(userId: string, limit: number = 10): GameSession[] {
    const sessions: GameSession[] = [];
    for (const session of this.sessions.values()) {
      if (session.userId === userId) {
        sessions.push(session);
      }
    }
    return sessions
      .sort((a, b) => b.startTime.getTime() - a.startTime.getTime())
      .slice(0, limit);
  }

  /**
   * Calculate session statistics
   */
  getSessionStats(sessionId: string): {
    duration: number;
    avgBetPerSpin: number;
    winRate: number;
    roi: number;
  } | null {
    const session = this.sessions.get(sessionId);
    if (!session) return null;

    const endTime = session.endTime || new Date();
    const duration = endTime.getTime() - session.startTime.getTime();
    const avgBetPerSpin = session.totalSpins > 0 ? session.totalBet / session.totalSpins : 0;
    const winRate = session.totalSpins > 0 ? (session.totalWins / session.totalSpins) * 100 : 0;
    const roi = session.totalBet > 0 ? ((session.netWinnings / session.totalBet) * 100) : 0;

    return {
      duration,
      avgBetPerSpin,
      winRate,
      roi,
    };
  }

  /**
   * Get global game statistics
   */
  getGlobalStats(): {
    totalEvents: number;
    totalSpins: number;
    totalWins: number;
    uniquePlayers: number;
    avgSpinsPerPlayer: number;
    avgWinRate: number;
  } {
    const uniquePlayers = new Set(this.events.map(e => e.userId));
    const totalSpins = this.events.filter(e => e.eventType === 'spin-completed').length;
    const totalWins = this.events.filter(e => e.eventType === 'win-detected').length;

    return {
      totalEvents: this.events.length,
      totalSpins,
      totalWins,
      uniquePlayers: uniquePlayers.size,
      avgSpinsPerPlayer: uniquePlayers.size > 0 ? totalSpins / uniquePlayers.size : 0,
      avgWinRate: totalSpins > 0 ? (totalWins / totalSpins) * 100 : 0,
    };
  }

  /**
   * Get game-specific statistics
   */
  getGameStats(gameId: string): {
    totalPlays: number;
    uniquePlayers: number;
    totalSpins: number;
    totalWins: number;
    avgWinRate: number;
  } {
    const gameEvents = this.events.filter(e => e.gameId === gameId);
    const uniquePlayers = new Set(gameEvents.map(e => e.userId));
    const totalSpins = gameEvents.filter(e => e.eventType === 'spin-completed').length;
    const totalWins = gameEvents.filter(e => e.eventType === 'win-detected').length;

    return {
      totalPlays: gameEvents.length,
      uniquePlayers: uniquePlayers.size,
      totalSpins,
      totalWins,
      avgWinRate: totalSpins > 0 ? (totalWins / totalSpins) * 100 : 0,
    };
  }

  /**
   * Clear all data (for testing)
   */
  clear(): void {
    this.events = [];
    this.sessions.clear();
    this.userStats.clear();
  }
}

export const gameEventSystem = new GameEventSystem();
