/**
 * Slot Game Engine - tRPC Router
 * Connects frontend slot game UI to backend RNG and wallet systems
 */

import { router, protectedProcedure } from '../_core/trpc.ts';
import { z } from 'zod';
import { RNGEngine } from '../slotEngine/rngEngine.ts';
import { GameGenerator } from '../slotEngine/gameGenerator.ts';
import { WalletIntegration } from '../slotEngine/walletIntegration.ts';
import type { GameConfig, SpinResult } from '../slotEngine/types.ts';

// In-memory game storage (in production, use database)
const gameLibrary = new Map<string, GameConfig>();

// Initialize with preset games
GameGenerator.generatePresetGames().forEach((game) => {
  gameLibrary.set(game.id, game);
});

export const slotEngineRouter = router({
  /**
   * Get all available games
   */
  listGames: protectedProcedure.query(async () => {
    return Array.from(gameLibrary.values()).filter((game) => game.enabled);
  }),

  /**
   * Get specific game details
   */
  getGame: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .query(async ({ input }) => {
      return gameLibrary.get(input.gameId) || null;
    }),

  /**
   * Create new game from config
   */
  createGame: protectedProcedure
    .input(
      z.object({
        name: z.string(),
        type: z.enum(['classic', 'video', 'megaways', 'hold_spin', 'bonus_feature', 'progressive']),
        reels: z.number().min(3).max(7),
        rows: z.number().min(2).max(7),
        rtp: z.number().min(85).max(98),
        volatility: z.enum(['low', 'medium', 'high', 'extreme']),
        theme: z.string(),
        features: z.array(z.string()),
      })
    )
    .mutation(async ({ input, ctx }) => {
      // Only admins can create games
      if (ctx.user.role !== 'admin') {
        throw new Error('Unauthorized');
      }

      const game = GameGenerator.generateGame(input);
      gameLibrary.set(game.id, game);
      return game;
    }),

  /**
   * Perform a spin
   */
  spin: protectedProcedure
    .input(
      z.object({
        gameId: z.string(),
        betAmount: z.number().positive(),
      })
    )
    .mutation(async ({ input, ctx }) => {
      const game = gameLibrary.get(input.gameId);
      if (!game) {
        throw new Error('Game not found');
      }

      // Validate bet amount
      if (input.betAmount < game.minBet || input.betAmount > game.maxBet) {
        throw new Error('Invalid bet amount');
      }

      // Generate spin outcome
      const outcome = RNGEngine.generateReelOutcome(game, input.betAmount, game.rtp);

      // Check for bonus trigger
      const bonusTriggered = RNGEngine.checkBonusTriggered(outcome.reels, game);

      // Handle cascading reels if enabled
      let cascadeInfo = { cascadeCount: 0, totalWin: 0 };
      if (game.cascadingReels) {
        cascadeInfo = RNGEngine.calculateCascadeWins(outcome.reels, game);
        outcome.winAmount += cascadeInfo.totalWin;
      }

      // Process wallet transaction
      const spinResult = await WalletIntegration.processSpin(
        {
          userId: ctx.user.id.toString(),
          gameId: input.gameId,
          betAmount: input.betAmount,
          freeSpinMode: false,
        },
        {
          winAmount: outcome.winAmount,
          multiplier: outcome.multiplier,
          reels: outcome.reels,
        }
      );

      return {
        ...spinResult,
        bonusTriggered,
        cascadeCount: cascadeInfo.cascadeCount,
      };
    }),

  /**
   * Perform a free spin
   */
  freeSpin: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .mutation(async ({ input, ctx }) => {
      const game = gameLibrary.get(input.gameId);
      if (!game) {
        throw new Error('Game not found');
      }

      // Generate spin outcome (no bet)
      const outcome = RNGEngine.generateReelOutcome(game, 0, game.rtp);

      // Check for bonus trigger
      const bonusTriggered = RNGEngine.checkBonusTriggered(outcome.reels, game);

      // Process free spin with wallet
      const spinResult = await WalletIntegration.processFreeSpin(
        ctx.user.id.toString(),
        input.gameId,
        {
          winAmount: outcome.winAmount,
          multiplier: outcome.multiplier,
          reels: outcome.reels,
        }
      );

      return {
        ...spinResult,
        bonusTriggered,
      };
    }),

  /**
   * Get player balance
   */
  getBalance: protectedProcedure.query(async ({ ctx }) => {
    return await WalletIntegration.getBalance(ctx.user.id.toString());
  }),

  /**
   * Add bonus to player
   */
  addBonus: protectedProcedure
    .input(
      z.object({
        userId: z.string(),
        amount: z.number().positive(),
        reason: z.string(),
      })
    )
    .mutation(async ({ input, ctx }) => {
      // Only admins can add bonuses
      if (ctx.user.role !== 'admin') {
        throw new Error('Unauthorized');
      }

      const newBalance = await WalletIntegration.addBonus(input.userId, input.amount, input.reason);
      return { newBalance };
    }),

  /**
   * Get game statistics
   */
  getGameStats: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .query(async ({ input }) => {
      const game = gameLibrary.get(input.gameId);
      if (!game) {
        throw new Error('Game not found');
      }

      return {
        gameId: game.id,
        name: game.name,
        type: game.type,
        rtp: game.rtp,
        volatility: game.volatility,
        reels: game.reels,
        rows: game.rows,
        features: game.features,
        theme: game.theme,
        enabled: game.enabled,
      };
    }),

  /**
   * Update game configuration (admin only)
   */
  updateGame: protectedProcedure
    .input(
      z.object({
        gameId: z.string(),
        updates: z.object({
          rtp: z.number().min(85).max(98).optional(),
          enabled: z.boolean().optional(),
          minBet: z.number().positive().optional(),
          maxBet: z.number().positive().optional(),
        }),
      })
    )
    .mutation(async ({ input, ctx }) => {
      // Only admins can update games
      if (ctx.user.role !== 'admin') {
        throw new Error('Unauthorized');
      }

      const game = gameLibrary.get(input.gameId);
      if (!game) {
        throw new Error('Game not found');
      }

      // Update game configuration
      Object.assign(game, input.updates);
      game.updatedAt = new Date();

      return game;
    }),

  /**
   * Delete game (admin only)
   */
  deleteGame: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .mutation(async ({ input, ctx }) => {
      // Only admins can delete games
      if (ctx.user.role !== 'admin') {
        throw new Error('Unauthorized');
      }

      gameLibrary.delete(input.gameId);
      return { success: true };
    }),

  /**
   * Get all games for admin
   */
  adminListGames: protectedProcedure.query(async ({ ctx }) => {
    if (ctx.user.role !== 'admin') {
      throw new Error('Unauthorized');
    }

    return Array.from(gameLibrary.values());
  }),

  /**
   * Verify transaction integrity
   */
  verifyTransaction: protectedProcedure
    .input(z.object({ expectedBalance: z.number() }))
    .query(async ({ input, ctx }) => {
      return await WalletIntegration.verifyTransaction(ctx.user.id.toString(), input.expectedBalance);
    }),
});
