import { EventEmitter } from 'events';

export type MilestoneType = 'level' | 'wins' | 'score' | 'streak';

export interface Milestone {
  type: MilestoneType;
  value: number;
  previousValue: number;
  achievementId?: string;
  reward?: {
    coins?: number;
    xp?: number;
    badge?: string;
  };
}

export interface MilestoneEvent {
  userId: string;
  milestone: Milestone;
  timestamp: Date;
  notified: boolean;
}

// Define milestone thresholds
const LEVEL_MILESTONES = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100];
const WIN_MILESTONES = [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
const SCORE_MILESTONES = [100000, 500000, 1000000, 5000000, 10000000, 50000000, 100000000];
const STREAK_MILESTONES = [5, 10, 25, 50, 100];

class MilestoneDetectionSystem extends EventEmitter {
  private milestoneHistory: Map<string, MilestoneEvent[]> = new Map();
  private userStats: Map<string, { level: number; wins: number; score: number; streak: number }> = new Map();
  private achievementMap: Map<string, string> = new Map(); // milestone-key -> achievementId

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

  /**
   * Initialize achievement mappings for milestones
   */
  private initializeAchievementMap(): void {
    // Level milestones
    LEVEL_MILESTONES.forEach(level => {
      this.achievementMap.set(`level-${level}`, `ach_level_${level}`);
    });

    // Win milestones
    WIN_MILESTONES.forEach(wins => {
      this.achievementMap.set(`wins-${wins}`, `ach_wins_${wins}`);
    });

    // Score milestones
    SCORE_MILESTONES.forEach(score => {
      this.achievementMap.set(`score-${score}`, `ach_score_${score}`);
    });

    // Streak milestones
    STREAK_MILESTONES.forEach(streak => {
      this.achievementMap.set(`streak-${streak}`, `ach_streak_${streak}`);
    });
  }

  /**
   * Check and record level milestone
   */
  checkLevelMilestone(userId: string, newLevel: number, previousLevel: number = 0): MilestoneEvent | null {
    const stats = this.userStats.get(userId) || { level: previousLevel, wins: 0, score: 0, streak: 0 };
    stats.level = newLevel;
    this.userStats.set(userId, stats);

    for (const milestone of LEVEL_MILESTONES) {
      if (newLevel >= milestone && previousLevel < milestone) {
        return this.recordMilestone(userId, {
          type: 'level',
          value: milestone,
          previousValue: previousLevel,
          achievementId: this.achievementMap.get(`level-${milestone}`),
          reward: {
            coins: milestone * 10, // 50 coins for level 5, 100 for level 10, etc.
            xp: milestone * 50,
          },
        });
      }
    }

    return null;
  }

  /**
   * Check and record win milestone
   */
  checkWinMilestone(userId: string, newWins: number, previousWins: number = 0): MilestoneEvent | null {
    const stats = this.userStats.get(userId) || { level: 0, wins: previousWins, score: 0, streak: 0 };
    stats.wins = newWins;
    this.userStats.set(userId, stats);

    for (const milestone of WIN_MILESTONES) {
      if (newWins >= milestone && previousWins < milestone) {
        return this.recordMilestone(userId, {
          type: 'wins',
          value: milestone,
          previousValue: previousWins,
          achievementId: this.achievementMap.get(`wins-${milestone}`),
          reward: {
            coins: Math.floor(milestone / 10), // 1 coin for 10 wins, 5 for 50, etc.
            xp: milestone * 10,
          },
        });
      }
    }

    return null;
  }

  /**
   * Check and record score milestone
   */
  checkScoreMilestone(userId: string, newScore: number, previousScore: number = 0): MilestoneEvent | null {
    const stats = this.userStats.get(userId) || { level: 0, wins: 0, score: previousScore, streak: 0 };
    stats.score = newScore;
    this.userStats.set(userId, stats);

    for (const milestone of SCORE_MILESTONES) {
      if (newScore >= milestone && previousScore < milestone) {
        return this.recordMilestone(userId, {
          type: 'score',
          value: milestone,
          previousValue: previousScore,
          achievementId: this.achievementMap.get(`score-${milestone}`),
          reward: {
            coins: Math.floor(milestone / 100000), // 1 coin for 100k, 5 for 500k, etc.
            xp: Math.floor(milestone / 10000),
          },
        });
      }
    }

    return null;
  }

  /**
   * Check and record streak milestone
   */
  checkStreakMilestone(userId: string, newStreak: number, previousStreak: number = 0): MilestoneEvent | null {
    const stats = this.userStats.get(userId) || { level: 0, wins: 0, score: 0, streak: previousStreak };
    stats.streak = newStreak;
    this.userStats.set(userId, stats);

    for (const milestone of STREAK_MILESTONES) {
      if (newStreak >= milestone && previousStreak < milestone) {
        return this.recordMilestone(userId, {
          type: 'streak',
          value: milestone,
          previousValue: previousStreak,
          achievementId: this.achievementMap.get(`streak-${milestone}`),
          reward: {
            coins: milestone * 5, // 25 coins for 5-streak, 50 for 10-streak, etc.
            xp: milestone * 25,
          },
        });
      }
    }

    return null;
  }

  /**
   * Record a milestone event
   */
  private recordMilestone(userId: string, milestone: Milestone): MilestoneEvent {
    const event: MilestoneEvent = {
      userId,
      milestone,
      timestamp: new Date(),
      notified: false,
    };

    if (!this.milestoneHistory.has(userId)) {
      this.milestoneHistory.set(userId, []);
    }
    this.milestoneHistory.get(userId)!.push(event);

    this.emit('milestone-reached', event);

    return event;
  }

  /**
   * Get all milestones reached by user
   */
  getUserMilestones(userId: string): MilestoneEvent[] {
    return this.milestoneHistory.get(userId) || [];
  }

  /**
   * Get milestones by type
   */
  getMilestonesByType(userId: string, type: MilestoneType): MilestoneEvent[] {
    const milestones = this.milestoneHistory.get(userId) || [];
    return milestones.filter(m => m.milestone.type === type);
  }

  /**
   * Get next milestone for user
   */
  getNextMilestone(userId: string, type: MilestoneType): number | null {
    const stats = this.userStats.get(userId);
    if (!stats) return null;

    const currentValue = type === 'level' ? stats.level : type === 'wins' ? stats.wins : type === 'score' ? stats.score : stats.streak;

    const milestones = type === 'level' ? LEVEL_MILESTONES : type === 'wins' ? WIN_MILESTONES : type === 'score' ? SCORE_MILESTONES : STREAK_MILESTONES;

    for (const milestone of milestones) {
      if (currentValue < milestone) {
        return milestone;
      }
    }

    return null;
  }

  /**
   * Get progress towards next milestone
   */
  getProgressToNextMilestone(
    userId: string,
    type: MilestoneType
  ): {
    current: number;
    next: number | null;
    progress: number;
    percentComplete: number;
  } | null {
    const stats = this.userStats.get(userId);
    if (!stats) return null;

    const currentValue = type === 'level' ? stats.level : type === 'wins' ? stats.wins : type === 'score' ? stats.score : stats.streak;

    const milestones = type === 'level' ? LEVEL_MILESTONES : type === 'wins' ? WIN_MILESTONES : type === 'score' ? SCORE_MILESTONES : STREAK_MILESTONES;

    let nextMilestone: number | null = null;
    let previousMilestone = 0;

    for (const milestone of milestones) {
      if (currentValue < milestone) {
        nextMilestone = milestone;
        break;
      }
      previousMilestone = milestone;
    }

    if (!nextMilestone) {
      return {
        current: currentValue,
        next: null,
        progress: 0,
        percentComplete: 100,
      };
    }

    const progress = currentValue - previousMilestone;
    const total = nextMilestone - previousMilestone;
    const percentComplete = (progress / total) * 100;

    return {
      current: currentValue,
      next: nextMilestone,
      progress,
      percentComplete,
    };
  }

  /**
   * Get all upcoming milestones
   */
  getUpcomingMilestones(userId: string, limit: number = 5): Array<{ type: MilestoneType; value: number }> {
    const stats = this.userStats.get(userId);
    if (!stats) return [];

    const upcoming: Array<{ type: MilestoneType; value: number }> = [];

    const types: MilestoneType[] = ['level', 'wins', 'score', 'streak'];

    for (const type of types) {
      const next = this.getNextMilestone(userId, type);
      if (next) {
        upcoming.push({ type, value: next });
      }
    }

    return upcoming.slice(0, limit);
  }

  /**
   * Get milestone statistics
   */
  getMilestoneStats(userId: string): {
    totalMilestones: number;
    byType: Record<MilestoneType, number>;
    totalRewardsEarned: { coins: number; xp: number };
    lastMilestone?: MilestoneEvent;
  } {
    const milestones = this.milestoneHistory.get(userId) || [];

    const byType: Record<MilestoneType, number> = {
      level: 0,
      wins: 0,
      score: 0,
      streak: 0,
    };

    let totalCoins = 0;
    let totalXp = 0;

    for (const event of milestones) {
      byType[event.milestone.type]++;
      if (event.milestone.reward?.coins) totalCoins += event.milestone.reward.coins;
      if (event.milestone.reward?.xp) totalXp += event.milestone.reward.xp;
    }

    return {
      totalMilestones: milestones.length,
      byType,
      totalRewardsEarned: { coins: totalCoins, xp: totalXp },
      lastMilestone: milestones[milestones.length - 1],
    };
  }

  /**
   * Mark milestone as notified
   */
  markAsNotified(userId: string, milestoneIndex: number): void {
    const milestones = this.milestoneHistory.get(userId);
    if (milestones && milestones[milestoneIndex]) {
      milestones[milestoneIndex].notified = true;
    }
  }

  /**
   * Clear all data (for testing)
   */
  clear(): void {
    this.milestoneHistory.clear();
    this.userStats.clear();
  }
}

export const milestoneDetectionSystem = new MilestoneDetectionSystem();
