/**
 * HTML5 Game Template
 * Base template for creating new HTML5 games with PixiJS renderer
 * Includes wallet integration, RNG, and transaction safety
 */

export interface HTML5GameConfig {
  id: string;
  name: string;
  description: string;
  thumbnail: string;
  rtp: number;
  volatility: 'low' | 'medium' | 'high';
  minBet: number;
  maxBet: number;
  reels: number;
  rows: number;
  paylines: number;
  symbols: GameSymbol[];
  features: GameFeature[];
  bonusRounds?: BonusRound[];
}

export interface GameSymbol {
  id: string;
  name: string;
  emoji: string;
  payouts: Record<number, number>; // reels -> multiplier
  isWild?: boolean;
  isScatter?: boolean;
}

export interface GameFeature {
  name: string;
  description: string;
  triggerCondition: string;
  reward?: number;
}

export interface BonusRound {
  id: string;
  name: string;
  type: 'free_spins' | 'pick_em' | 'wheel' | 'respin';
  triggerSymbol: string;
  triggerCount: number;
  reward: number;
}

export interface GameSpinResult {
  reels: string[][];
  wins: WinLine[];
  totalWin: number;
  bonusTriggered?: BonusRound;
  freeSpinsAwarded?: number;
}

export interface WinLine {
  paylineId: number;
  symbols: string[];
  multiplier: number;
  payout: number;
}

export class HTML5GameTemplate {
  private config: HTML5GameConfig;

  constructor(config: HTML5GameConfig) {
    this.config = config;
  }

  /**
   * Get game configuration
   */
  getConfig(): HTML5GameConfig {
    return this.config;
  }

  /**
   * Validate bet amount
   */
  validateBet(bet: number): { valid: boolean; error?: string } {
    if (bet < this.config.minBet) {
      return { valid: false, error: `Minimum bet is ${this.config.minBet}` };
    }
    if (bet > this.config.maxBet) {
      return { valid: false, error: `Maximum bet is ${this.config.maxBet}` };
    }
    return { valid: true };
  }

  /**
   * Generate random reel outcome
   */
  generateReelOutcome(): string[][] {
    const reels: string[][] = [];
    for (let i = 0; i < this.config.reels; i++) {
      const reel: string[] = [];
      for (let j = 0; j < this.config.rows; j++) {
        const randomIndex = Math.floor(Math.random() * this.config.symbols.length);
        reel.push(this.config.symbols[randomIndex].id);
      }
      reels.push(reel);
    }
    return reels;
  }

  /**
   * Calculate payline wins
   */
  calculatePaylineWins(reels: string[][], bet: number): WinLine[] {
    const wins: WinLine[] = [];

    for (let paylineId = 0; paylineId < this.config.paylines; paylineId++) {
      const paylineSymbols = this.getPaylineSymbols(reels, paylineId);
      const matchCount = this.countConsecutiveMatches(paylineSymbols);

      if (matchCount >= 3) {
        const symbol = this.config.symbols.find((s) => s.id === paylineSymbols[0]);
        if (symbol) {
          const multiplier = symbol.payouts[matchCount] || 0;
          const payout = bet * multiplier;

          wins.push({
            paylineId,
            symbols: paylineSymbols.slice(0, matchCount),
            multiplier,
            payout,
          });
        }
      }
    }

    return wins;
  }

  /**
   * Get symbols on a payline
   */
  private getPaylineSymbols(reels: string[][], paylineId: number): string[] {
    const symbols: string[] = [];
    const paylinePattern = this.getPaylinePattern(paylineId);

    for (let i = 0; i < this.config.reels; i++) {
      const rowIndex = paylinePattern[i] || 0;
      if (rowIndex < this.config.rows) {
        symbols.push(reels[i][rowIndex]);
      }
    }

    return symbols;
  }

  /**
   * Get payline pattern (which row for each reel)
   */
  private getPaylinePattern(paylineId: number): number[] {
    // Standard 9-payline pattern for 3x3 grid
    const patterns: Record<number, number[]> = {
      0: [0, 0, 0], // top
      1: [1, 1, 1], // middle
      2: [2, 2, 2], // bottom
      3: [0, 1, 2], // diagonal down
      4: [2, 1, 0], // diagonal up
      5: [0, 0, 1], // top-top-middle
      6: [1, 0, 1], // middle-top-middle
      7: [1, 2, 1], // middle-bottom-middle
      8: [2, 2, 1], // bottom-bottom-middle
    };

    return patterns[paylineId] || [1, 1, 1];
  }

  /**
   * Count consecutive matching symbols
   */
  private countConsecutiveMatches(symbols: string[]): number {
    if (symbols.length === 0) return 0;

    let count = 1;
    for (let i = 1; i < symbols.length; i++) {
      if (symbols[i] === symbols[0]) {
        count++;
      } else {
        break;
      }
    }

    return count;
  }

  /**
   * Check for bonus trigger
   */
  checkBonusTrigger(reels: string[][]): BonusRound | null {
    if (!this.config.bonusRounds) return null;

    for (const bonus of this.config.bonusRounds) {
      const triggerSymbol = this.config.symbols.find((s) => s.id === bonus.triggerSymbol);
      if (!triggerSymbol) continue;

      let scatterCount = 0;
      for (const reel of reels) {
        for (const symbol of reel) {
          if (symbol === bonus.triggerSymbol) {
            scatterCount++;
          }
        }
      }

      if (scatterCount >= bonus.triggerCount) {
        return bonus;
      }
    }

    return null;
  }

  /**
   * Calculate total win
   */
  calculateTotalWin(wins: WinLine[], bet: number): number {
    return wins.reduce((total, win) => total + win.payout, 0);
  }

  /**
   * Process spin result
   */
  processSpin(bet: number): GameSpinResult {
    const reels = this.generateReelOutcome();
    const wins = this.calculatePaylineWins(reels, bet);
    const totalWin = this.calculateTotalWin(wins, bet);
    const bonusTriggered = this.checkBonusTrigger(reels);

    return {
      reels,
      wins,
      totalWin,
      bonusTriggered,
      freeSpinsAwarded: bonusTriggered?.type === 'free_spins' ? bonusTriggered.reward : undefined,
    };
  }

  /**
   * Get game statistics
   */
  getGameStats(): {
    rtp: number;
    volatility: string;
    expectedReturn: number;
    hitFrequency: number;
  } {
    return {
      rtp: this.config.rtp,
      volatility: this.config.volatility,
      expectedReturn: this.config.rtp / 100,
      hitFrequency: 0.25, // Approximate 25% hit rate
    };
  }

  /**
   * Validate game configuration
   */
  validateConfig(): { valid: boolean; errors: string[] } {
    const errors: string[] = [];

    if (!this.config.id) errors.push('Game ID is required');
    if (!this.config.name) errors.push('Game name is required');
    if (this.config.rtp < 80 || this.config.rtp > 98) errors.push('RTP must be between 80-98%');
    if (this.config.minBet <= 0) errors.push('Min bet must be greater than 0');
    if (this.config.maxBet <= this.config.minBet) errors.push('Max bet must be greater than min bet');
    if (this.config.symbols.length === 0) errors.push('At least one symbol is required');
    if (this.config.paylines === 0) errors.push('At least one payline is required');

    return {
      valid: errors.length === 0,
      errors,
    };
  }
}

/**
 * Create a new HTML5 game from template
 */
export function createHTML5Game(config: HTML5GameConfig): HTML5GameTemplate {
  const game = new HTML5GameTemplate(config);

  const validation = game.validateConfig();
  if (!validation.valid) {
    throw new Error(`Invalid game configuration: ${validation.errors.join(', ')}`);
  }

  return game;
}
