import { EventEmitter } from 'events';

export interface BatchJob {
  id: string;
  conceptId: string;
  variantCount: number;
  status: 'queued' | 'processing' | 'completed' | 'failed';
  generatedGames: string[];
  progress: number;
  startedAt: Date;
  completedAt?: Date;
  error?: string;
  estimatedTimeRemaining?: number;
}

export interface GameAnalytics {
  gameId: string;
  totalPlays: number;
  totalPlayers: number;
  avgPlaytime: number;
  engagementScore: number;
  retentionRate: number;
  winRate: number;
  avgWinAmount: number;
  totalRevenue: number;
  lastUpdated: Date;
}

export interface ABTestResult {
  testId: string;
  gameAId: string;
  gameBId: string;
  status: 'running' | 'completed' | 'paused';
  startDate: Date;
  endDate?: Date;
  sampleSizeA: number;
  sampleSizeB: number;
  engagementA: number;
  engagementB: number;
  winnerConfidence: number;
  winner?: 'A' | 'B' | 'tie';
}

/**
 * Batch Game Generation System
 * Manages batch creation of game variants and performance analytics
 */
export class BatchGameGenerationSystem extends EventEmitter {
  private jobs: Map<string, BatchJob> = new Map();
  private analytics: Map<string, GameAnalytics> = new Map();
  private abTests: Map<string, ABTestResult> = new Map();
  private jobQueue: string[] = [];
  private processingJob: string | null = null;

  constructor() {
    super();
    console.log('[BatchGeneration] System initialized');
    this.startJobProcessor();
  }

  /**
   * Create batch generation job
   */
  createBatchJob(conceptId: string, variantCount: number): BatchJob {
    const job: BatchJob = {
      id: `batch-${Date.now()}`,
      conceptId,
      variantCount,
      status: 'queued',
      generatedGames: [],
      progress: 0,
      startedAt: new Date(),
    };

    this.jobs.set(job.id, job);
    this.jobQueue.push(job.id);

    this.emit('jobCreated', job);
    console.log(`[BatchGeneration] Job created: ${job.id} (${variantCount} variants)`);

    return job;
  }

  /**
   * Get job status
   */
  getJobStatus(jobId: string): BatchJob | undefined {
    return this.jobs.get(jobId);
  }

  /**
   * Start job processor
   */
  private startJobProcessor(): void {
    setInterval(() => {
      if (this.processingJob === null && this.jobQueue.length > 0) {
        const jobId = this.jobQueue.shift();
        if (jobId) {
          this.processJob(jobId);
        }
      }
    }, 1000);
  }

  /**
   * Process batch job
   */
  private async processJob(jobId: string): Promise<void> {
    const job = this.jobs.get(jobId);
    if (!job) return;

    this.processingJob = jobId;
    job.status = 'processing';
    job.startedAt = new Date();

    this.emit('jobStarted', job);
    console.log(`[BatchGeneration] Processing job: ${jobId}`);

    try {
      // Simulate game generation
      for (let i = 0; i < job.variantCount; i++) {
        // Generate game variant
        const gameId = `game-variant-${jobId}-${i}`;
        job.generatedGames.push(gameId);
        job.progress = Math.round(((i + 1) / job.variantCount) * 100);
        job.estimatedTimeRemaining = Math.max(0, (job.variantCount - i - 1) * 2);

        this.emit('jobProgress', job);

        // Simulate processing time
        await new Promise((resolve) => setTimeout(resolve, 2000));
      }

      job.status = 'completed';
      job.completedAt = new Date();
      job.progress = 100;

      this.emit('jobCompleted', job);
      console.log(`[BatchGeneration] Job completed: ${jobId} (${job.generatedGames.length} games)`);
    } catch (error) {
      job.status = 'failed';
      job.error = String(error);
      job.completedAt = new Date();

      this.emit('jobFailed', job);
      console.error(`[BatchGeneration] Job failed: ${jobId}`, error);
    } finally {
      this.processingJob = null;
    }
  }

  /**
   * Record game analytics
   */
  recordAnalytics(gameId: string, data: Partial<GameAnalytics>): GameAnalytics {
    let analytics = this.analytics.get(gameId);

    if (!analytics) {
      analytics = {
        gameId,
        totalPlays: 0,
        totalPlayers: 0,
        avgPlaytime: 0,
        engagementScore: 0,
        retentionRate: 0,
        winRate: 0,
        avgWinAmount: 0,
        totalRevenue: 0,
        lastUpdated: new Date(),
      };
    }

    // Update analytics
    if (data.totalPlays !== undefined) analytics.totalPlays = data.totalPlays;
    if (data.totalPlayers !== undefined) analytics.totalPlayers = data.totalPlayers;
    if (data.avgPlaytime !== undefined) analytics.avgPlaytime = data.avgPlaytime;
    if (data.engagementScore !== undefined) analytics.engagementScore = data.engagementScore;
    if (data.retentionRate !== undefined) analytics.retentionRate = data.retentionRate;
    if (data.winRate !== undefined) analytics.winRate = data.winRate;
    if (data.avgWinAmount !== undefined) analytics.avgWinAmount = data.avgWinAmount;
    if (data.totalRevenue !== undefined) analytics.totalRevenue = data.totalRevenue;

    analytics.lastUpdated = new Date();
    this.analytics.set(gameId, analytics);

    this.emit('analyticsRecorded', analytics);

    return analytics;
  }

  /**
   * Get game analytics
   */
  getAnalytics(gameId: string): GameAnalytics | undefined {
    return this.analytics.get(gameId);
  }

  /**
   * Create A/B test
   */
  createABTest(gameAId: string, gameBId: string): ABTestResult {
    const test: ABTestResult = {
      testId: `abtest-${Date.now()}`,
      gameAId,
      gameBId,
      status: 'running',
      startDate: new Date(),
      sampleSizeA: 0,
      sampleSizeB: 0,
      engagementA: 0,
      engagementB: 0,
      winnerConfidence: 0,
    };

    this.abTests.set(test.testId, test);

    this.emit('abTestCreated', test);
    console.log(`[BatchGeneration] A/B test created: ${test.testId}`);

    return test;
  }

  /**
   * Update A/B test results
   */
  updateABTestResults(testId: string, results: Partial<ABTestResult>): ABTestResult | undefined {
    const test = this.abTests.get(testId);
    if (!test) return undefined;

    if (results.sampleSizeA !== undefined) test.sampleSizeA = results.sampleSizeA;
    if (results.sampleSizeB !== undefined) test.sampleSizeB = results.sampleSizeB;
    if (results.engagementA !== undefined) test.engagementA = results.engagementA;
    if (results.engagementB !== undefined) test.engagementB = results.engagementB;

    // Calculate winner and confidence
    if (test.sampleSizeA > 0 && test.sampleSizeB > 0) {
      const diff = Math.abs(test.engagementA - test.engagementB);
      const avgEngagement = (test.engagementA + test.engagementB) / 2;
      test.winnerConfidence = avgEngagement > 0 ? (diff / avgEngagement) * 100 : 0;

      if (test.winnerConfidence > 5) {
        test.winner = test.engagementA > test.engagementB ? 'A' : 'B';
      }
    }

    this.emit('abTestUpdated', test);

    return test;
  }

  /**
   * End A/B test
   */
  endABTest(testId: string): ABTestResult | undefined {
    const test = this.abTests.get(testId);
    if (!test) return undefined;

    test.status = 'completed';
    test.endDate = new Date();

    this.emit('abTestCompleted', test);
    console.log(`[BatchGeneration] A/B test completed: ${testId}, winner: ${test.winner}`);

    return test;
  }

  /**
   * Get A/B test results
   */
  getABTestResults(testId: string): ABTestResult | undefined {
    return this.abTests.get(testId);
  }

  /**
   * Get top performing games
   */
  getTopPerformingGames(limit: number = 10): GameAnalytics[] {
    return Array.from(this.analytics.values())
      .sort((a, b) => b.engagementScore - a.engagementScore)
      .slice(0, limit);
  }

  /**
   * Get games by engagement score
   */
  getGamesByEngagement(minScore: number, maxScore: number): GameAnalytics[] {
    return Array.from(this.analytics.values()).filter((a) => a.engagementScore >= minScore && a.engagementScore <= maxScore);
  }

  /**
   * Calculate batch performance metrics
   */
  calculateBatchMetrics(jobId: string) {
    const job = this.jobs.get(jobId);
    if (!job || job.status !== 'completed') return null;

    const gameAnalytics = job.generatedGames
      .map((gameId) => this.analytics.get(gameId))
      .filter((a) => a !== undefined) as GameAnalytics[];

    if (gameAnalytics.length === 0) return null;

    const avgEngagement = gameAnalytics.reduce((sum, a) => sum + a.engagementScore, 0) / gameAnalytics.length;
    const avgRetention = gameAnalytics.reduce((sum, a) => sum + a.retentionRate, 0) / gameAnalytics.length;
    const totalRevenue = gameAnalytics.reduce((sum, a) => sum + a.totalRevenue, 0);
    const bestPerformer = gameAnalytics.reduce((best, a) => (a.engagementScore > best.engagementScore ? a : best));

    return {
      batchId: jobId,
      gameCount: gameAnalytics.length,
      avgEngagement,
      avgRetention,
      totalRevenue,
      bestPerformer: bestPerformer.gameId,
      bestEngagement: bestPerformer.engagementScore,
    };
  }

  /**
   * Get batch job statistics
   */
  getBatchStats() {
    return {
      totalJobs: this.jobs.size,
      completedJobs: Array.from(this.jobs.values()).filter((j) => j.status === 'completed').length,
      failedJobs: Array.from(this.jobs.values()).filter((j) => j.status === 'failed').length,
      totalGamesGenerated: Array.from(this.jobs.values()).reduce((sum, j) => sum + j.generatedGames.length, 0),
      queuedJobs: this.jobQueue.length,
      processingJob: this.processingJob,
    };
  }

  /**
   * Clear all data
   */
  clear(): void {
    this.jobs.clear();
    this.analytics.clear();
    this.abTests.clear();
    this.jobQueue = [];
    this.processingJob = null;
    console.log('[BatchGeneration] System cleared');
  }
}

export const batchGameGenerationSystem = new BatchGameGenerationSystem();
