/**
 * Bonus Features System
 * Handles free spins, multipliers, bonus games, sticky wilds, and expanding reels
 */

import { SlotMachine, SpinResult, GameConfig } from "./gameEngine.ts";

// ─── FREE SPINS FEATURE ───────────────────────────────────────────────────────

export interface FreeSpinSession {
  userId: number;
  gameId: number;
  spinsRemaining: number;
  multiplier: number; // Multiplier applied to all wins during free spins
  startedAt: Date;
  totalWinsAccumulated: number;
}

export class FreeSpinManager {
  private sessions = new Map<string, FreeSpinSession>();

  startFreeSpins(
    userId: number,
    gameId: number,
    count: number,
    multiplier: number = 1
  ): FreeSpinSession {
    const key = `${userId}-${gameId}`;
    const session: FreeSpinSession = {
      userId,
      gameId,
      spinsRemaining: count,
      multiplier,
      startedAt: new Date(),
      totalWinsAccumulated: 0,
    };
    this.sessions.set(key, session);
    return session;
  }

  getSession(userId: number, gameId: number): FreeSpinSession | undefined {
    return this.sessions.get(`${userId}-${gameId}`);
  }

  consumeSpin(userId: number, gameId: number, winAmount: number): FreeSpinSession | null {
    const key = `${userId}-${gameId}`;
    const session = this.sessions.get(key);
    if (!session) return null;

    session.spinsRemaining--;
    session.totalWinsAccumulated += winAmount;

    if (session.spinsRemaining <= 0) {
      this.sessions.delete(key);
      return null; // Session ended
    }

    return session;
  }

  endSession(userId: number, gameId: number): FreeSpinSession | undefined {
    const key = `${userId}-${gameId}`;
    return this.sessions.get(key);
  }
}

// ─── MULTIPLIER SYSTEM ────────────────────────────────────────────────────────

export interface MultiplierBoost {
  userId: number;
  gameId?: number; // null = global multiplier
  multiplier: number;
  expiresAt: Date;
  source: "vip_tier" | "event" | "promotion" | "achievement";
}

export class MultiplierManager {
  private boosts = new Map<string, MultiplierBoost[]>();

  addBoost(boost: MultiplierBoost): void {
    const key = boost.gameId ? `${boost.userId}-${boost.gameId}` : `${boost.userId}-global`;
    const existing = this.boosts.get(key) || [];
    existing.push(boost);
    this.boosts.set(key, existing);
  }

  getActiveMultiplier(userId: number, gameId?: number): number {
    const now = new Date();
    const key = gameId ? `${userId}-${gameId}` : `${userId}-global`;

    // Get game-specific boosts
    let multiplier = 1;
    const gameBoosts = this.boosts.get(key) || [];
    for (const boost of gameBoosts) {
      if (boost.expiresAt > now) {
        multiplier *= boost.multiplier;
      }
    }

    // Also check global boosts if looking for game-specific
    if (gameId) {
      const globalKey = `${userId}-global`;
      const globalBoosts = this.boosts.get(globalKey) || [];
      for (const boost of globalBoosts) {
        if (boost.expiresAt > now) {
          multiplier *= boost.multiplier;
        }
      }
    }

    return multiplier;
  }

  removeExpiredBoosts(userId: number): void {
    const now = new Date();
    const entries = Array.from(this.boosts.entries());
    for (const [key, boosts] of entries) {
      const active = boosts.filter((b: MultiplierBoost) => b.expiresAt > now);
      if (active.length === 0) {
        this.boosts.delete(key);
      } else {
        this.boosts.set(key, active);
      }
    }
  }
}

// ─── BONUS GAME FEATURE ────────────────────────────────────────────────────────

export enum BonusGameType {
  PICK_AND_CLICK = "pick_and_click",
  WHEEL_SPIN = "wheel_spin",
  CARD_FLIP = "card_flip",
  LADDER_CLIMB = "ladder_climb",
}

export interface BonusGame {
  type: BonusGameType;
  multiplier: number;
  prizePool: number[];
}

export interface BonusGameResult {
  selectedIndex: number;
  prizeAmount: number;
  totalWon: number;
}

export class BonusGameEngine {
  /**
   * Execute pick-and-click bonus game
   * Player picks from hidden items, each reveals a prize
   */
  static executePickAndClick(
    prizePool: number[],
    playerPicks: number[]
  ): BonusGameResult[] {
    const results: BonusGameResult[] = [];
    let totalWon = 0;

    for (const pick of playerPicks) {
      if (pick < 0 || pick >= prizePool.length) continue;

      const prizeAmount = prizePool[pick];
      totalWon += prizeAmount;

      results.push({
        selectedIndex: pick,
        prizeAmount,
        totalWon,
      });
    }

    return results;
  }

  /**
   * Execute wheel spin bonus game
   * Spin a wheel with various prize segments
   */
  static executeWheelSpin(segments: number[], spinCount: number = 1): BonusGameResult {
    let totalWon = 0;

    for (let i = 0; i < spinCount; i++) {
      const selectedIndex = Math.floor(Math.random() * segments.length);
      totalWon += segments[selectedIndex];
    }

    return {
      selectedIndex: Math.floor(Math.random() * segments.length),
      prizeAmount: segments[Math.floor(Math.random() * segments.length)],
      totalWon,
    };
  }

  /**
   * Execute card flip bonus game
   * Match pairs of cards to reveal prizes
   */
  static executeCardFlip(prizePool: number[], maxFlips: number): BonusGameResult {
    const cards = [...prizePool, ...prizePool]; // Create pairs
    let totalWon = 0;
    let flipsUsed = 0;
    const revealed = new Set<number>();

    while (flipsUsed < maxFlips && revealed.size < cards.length) {
      const cardIndex = Math.floor(Math.random() * cards.length);
      if (!revealed.has(cardIndex)) {
        revealed.add(cardIndex);
        totalWon += cards[cardIndex];
        flipsUsed++;
      }
    }

    return {
      selectedIndex: 0,
      prizeAmount: totalWon / Math.min(flipsUsed, maxFlips),
      totalWon,
    };
  }
}

// ─── STICKY WILDS FEATURE ─────────────────────────────────────────────────────

export interface StickyWildSession {
  userId: number;
  gameId: number;
  stickyPositions: Array<{ reel: number; row: number }>;
  spinsRemaining: number;
  expiresAt: Date;
}

export class StickyWildManager {
  private sessions = new Map<string, StickyWildSession>();

  addStickyWild(
    userId: number,
    gameId: number,
    reel: number,
    row: number,
    spinsRemaining: number = 3
  ): StickyWildSession {
    const key = `${userId}-${gameId}`;
    let session = this.sessions.get(key);

    if (!session) {
      session = {
        userId,
        gameId,
        stickyPositions: [],
        spinsRemaining,
        expiresAt: new Date(Date.now() + 30 * 60 * 1000), // 30 min expiry
      };
    }

    session.stickyPositions.push({ reel, row });
    this.sessions.set(key, session);
    return session;
  }

  getSession(userId: number, gameId: number): StickyWildSession | undefined {
    return this.sessions.get(`${userId}-${gameId}`);
  }

  consumeSpin(userId: number, gameId: number): StickyWildSession | null {
    const key = `${userId}-${gameId}`;
    const session = this.sessions.get(key);
    if (!session) return null;

    session.spinsRemaining--;

    if (session.spinsRemaining <= 0 || session.expiresAt < new Date()) {
      this.sessions.delete(key);
      return null;
    }

    return session;
  }
}

// ─── EXPANDING REELS FEATURE ──────────────────────────────────────────────────

export interface ExpandingReelSession {
  userId: number;
  gameId: number;
  expandedReels: number[]; // Reel indices that are expanded
  multiplier: number;
  spinsRemaining: number;
}

export class ExpandingReelManager {
  private sessions = new Map<string, ExpandingReelSession>();

  startExpandedReels(
    userId: number,
    gameId: number,
    reelIndices: number[],
    multiplier: number = 2,
    spinsRemaining: number = 3
  ): ExpandingReelSession {
    const key = `${userId}-${gameId}`;
    const session: ExpandingReelSession = {
      userId,
      gameId,
      expandedReels: reelIndices,
      multiplier,
      spinsRemaining,
    };
    this.sessions.set(key, session);
    return session;
  }

  getSession(userId: number, gameId: number): ExpandingReelSession | undefined {
    return this.sessions.get(`${userId}-${gameId}`);
  }

  consumeSpin(userId: number, gameId: number): ExpandingReelSession | null {
    const key = `${userId}-${gameId}`;
    const session = this.sessions.get(key);
    if (!session) return null;

    session.spinsRemaining--;

    if (session.spinsRemaining <= 0) {
      this.sessions.delete(key);
      return null;
    }

    return session;
  }
}

// ─── PROGRESSIVE BONUS MULTIPLIER ─────────────────────────────────────────────

export class ProgressiveBonusMultiplier {
  private multiplier: number = 1;
  private maxMultiplier: number = 5;
  private incrementPerSpin: number = 0.1;
  private resetThreshold: number = 10; // Spins without win before reset

  private spinsWithoutWin: number = 0;

  recordSpin(hasWon: boolean): number {
    if (hasWon) {
      this.spinsWithoutWin = 0;
      this.multiplier = Math.min(this.multiplier + this.incrementPerSpin, this.maxMultiplier);
    } else {
      this.spinsWithoutWin++;
      if (this.spinsWithoutWin >= this.resetThreshold) {
        this.multiplier = 1;
        this.spinsWithoutWin = 0;
      }
    }

    return this.multiplier;
  }

  getMultiplier(): number {
    return this.multiplier;
  }

  reset(): void {
    this.multiplier = 1;
    this.spinsWithoutWin = 0;
  }
}

// ─── RETRIGGER SYSTEM ─────────────────────────────────────────────────────────

export interface RetriggerEvent {
  type: "free_spins" | "bonus_game" | "sticky_wilds" | "expanding_reels";
  additionalCount: number;
  multiplierBoost: number;
}

export class RetriggerDetector {
  /**
   * Check if spin result triggers additional bonus features
   */
  static detectRetriggers(
    spinResult: SpinResult,
    gameConfig: GameConfig
  ): RetriggerEvent[] {
    const retriggers: RetriggerEvent[] = [];

    // Check for scatter retrigger (3+ scatters during free spins = more free spins)
    if (spinResult.outcome === "free_spins" && spinResult.freeSpinsAwarded > 0) {
      retriggers.push({
        type: "free_spins",
        additionalCount: spinResult.freeSpinsAwarded,
        multiplierBoost: 1.5,
      });
    }

    // Check for bonus game retrigger
    if (spinResult.bonusTriggered) {
      retriggers.push({
        type: "bonus_game",
        additionalCount: 1,
        multiplierBoost: 2,
      });
    }

    return retriggers;
  }
}
