import { router, publicProcedure, protectedProcedure } from "../_core/trpc.ts";
import { z } from "zod";
import {
  getCasinoGames, getGameById, getProviders, recordGameSession,
  getRecentlyPlayed, getRtpOverride, debitWallet, creditWallet,
  writeAuditLog, getOrCreateWallet, getPlatformSetting
} from "../db.ts";
import { SlotMachine, createGameConfig, ProgressiveJackpot } from "../gameEngine.ts";
import { getBrandedGameBySlug, brandedToGameConfig, BRANDED_GAMES } from "../brandedGames.ts";

// Progressive jackpot tracker per game
const jackpots = new Map<number, ProgressiveJackpot>();

function getJackpot(gameId: number): ProgressiveJackpot {
  if (!jackpots.has(gameId)) {
    jackpots.set(gameId, new ProgressiveJackpot(10000, 0.01));
  }
  return jackpots.get(gameId)!;
}

export const casinoRouter = router({
  getGames: publicProcedure
    .input(z.object({
      limit: z.number().min(1).max(100).default(24),
      offset: z.number().min(0).default(0),
      category: z.string().optional(),
      provider: z.string().optional(),
      search: z.string().optional(),
      featured: z.boolean().optional(),
    }))
    .query(async ({ input }) => {
      return getCasinoGames(input);
    }),

  getGame: publicProcedure
    .input(z.object({ id: z.number() }))
    .query(async ({ input }) => {
      return getGameById(input.id);
    }),

  getProviders: publicProcedure.query(async () => {
    return getProviders();
  }),

  getRecentlyPlayed: protectedProcedure.query(async ({ ctx }) => {
    return getRecentlyPlayed(ctx.user.id);
  }),

  /**
   * Advanced spin with full game engine
   * Supports provably fair verification and bonus features
   */
  spin: protectedProcedure
    .input(z.object({
      gameId: z.number(),
      betAmount: z.number().min(0.01),
      currency: z.enum(["GC", "SC"]),
      clientSeed: z.string().optional(),
    }))
    .mutation(async ({ ctx, input }) => {
      const game = await getGameById(input.gameId);
      if (!game || !game.isActive) throw new Error("Game not available");

      // Validate bet limits
      const minBet = input.currency === "GC" ? parseFloat(game.minBetGc) : parseFloat(game.minBetSc);
      const maxBet = input.currency === "GC" ? parseFloat(game.maxBetGc) : parseFloat(game.maxBetSc);
      if (input.betAmount < minBet || input.betAmount > maxBet) {
        throw new Error(`Bet must be between ${minBet} and ${maxBet}`);
      }

      // Check balance
      const wallet = await getOrCreateWallet(ctx.user.id);
      const balance = parseFloat(input.currency === "GC" ? wallet.gcBalance : wallet.scBalance);
      if (balance < input.betAmount) throw new Error("Insufficient balance");

      // Get effective RTP
      const rtpOverride = await getRtpOverride(ctx.user.id, input.gameId);
      const effectiveRtp = rtpOverride ?? game.rtp;

      // Debit bet
      await debitWallet(ctx.user.id, input.currency, input.betAmount, "game_bet", `${game.title} - Spin`, String(input.gameId));

      // Create game engine — use branded config if this is a PlayCoinKrazy game
      const brandedTheme = game.provider === "PlayCoinKrazy" ? getBrandedGameBySlug(game.slug || "") : undefined;
      const gameConfig = brandedTheme
        ? (() => {
            const cfg = brandedToGameConfig(brandedTheme, input.gameId);
            cfg.rtp = effectiveRtp; // apply admin RTP override
            return cfg;
          })()
        : createGameConfig(
            input.gameId,
            game.reels || 5,
            3,
            game.paylines || 25,
            effectiveRtp,
            (game.volatility as any) || "medium"
          );

      const slotMachine = new SlotMachine(gameConfig);

      // Generate server seed (in production, use secure random)
      const serverSeed = Math.random().toString(36).substring(2, 15);
      const clientSeed = input.clientSeed || Math.random().toString(36).substring(2, 15);

      // Execute spin
      const spinResult = slotMachine.spin(input.betAmount, serverSeed, clientSeed);

      // Add to progressive jackpot
      const jackpot = getJackpot(input.gameId);
      jackpot.addBet(input.betAmount);

      let finalWinAmount = spinResult.winAmount;

      // Check if jackpot won (rare)
      if (spinResult.outcome === "jackpot" && Math.random() < 0.001) {
        const jackpotAmount = jackpot.winJackpot();
        finalWinAmount = spinResult.winAmount + jackpotAmount;
      }

      // Enforce max win cap per spin (admin-configurable)
      const maxWinKey = input.currency === "SC" ? "max_sc_win_per_spin" : "max_gc_win_per_spin";
      const maxWinSetting = await getPlatformSetting(maxWinKey);
      const maxWinCap = maxWinSetting ? parseFloat(maxWinSetting) : (input.currency === "SC" ? 20 : 0);
      let cappedWin = false;
      if (maxWinCap > 0 && finalWinAmount > maxWinCap) {
        finalWinAmount = maxWinCap;
        cappedWin = true;
      }

      // Credit win
      if (finalWinAmount > 0) {
        await creditWallet(
          ctx.user.id,
          input.currency,
          finalWinAmount,
          "game_win",
          `${game.title} - Win x${spinResult.multiplier.toFixed(2)}`,
          String(input.gameId)
        );
      }

      // Record game win transaction
      await db.transactions.insert({
        userId: ctx.user.id,
        type: 'game_win',
        currency: input.currency,
        amount: finalWinAmount,
        referenceId: `game_${input.gameId}`,
        referenceType: 'game_win',
        metadata: {
          gameId: input.gameId,
          betAmount: input.betAmount,
          multiplier: spinResult.multiplier,
          outcome: spinResult,
          rtpApplied: effectiveRtp,
        },
      });

      // Get updated balance
      const updatedWallet = await getOrCreateWallet(ctx.user.id);

      return {
        reels: spinResult.reels,
        winAmount: finalWinAmount,
        multiplier: spinResult.multiplier,
        winningPaylines: spinResult.winningPaylines,
        outcome: spinResult.outcome,
        freeSpinsAwarded: spinResult.freeSpinsAwarded,
        bonusTriggered: spinResult.bonusTriggered,
        betAmount: input.betAmount,
        netResult: finalWinAmount - input.betAmount,
        newBalance: parseFloat(input.currency === "GC" ? updatedWallet.gcBalance : updatedWallet.scBalance),
        seed: spinResult.seed,
        hash: spinResult.hash,
        jackpotAmount: jackpot.getAmount(),
        cappedWin,
        brandedTheme: brandedTheme ? {
          slug: brandedTheme.slug,
          gradient: brandedTheme.gradient,
          accentColor: brandedTheme.accentColor,
          themeEmoji: brandedTheme.themeEmoji,
          symbols: brandedTheme.symbols.map(s => ({ id: s.id, name: s.name, emoji: s.emoji, type: s.type })),
        } : undefined,
      };
    }),

  /**
   * Get branded game theme data for PlayCoinKrazy games
   */
  getBrandedTheme: publicProcedure
    .input(z.object({ gameId: z.number() }))
    .query(async ({ input }) => {
      const game = await getGameById(input.gameId);
      if (!game) return null;
      const theme = game.provider === "PlayCoinKrazy" ? getBrandedGameBySlug(game.slug || "") : undefined;
      if (!theme) return null;
      return {
        slug: theme.slug,
        title: theme.title,
        description: theme.description,
        gradient: theme.gradient,
        accentColor: theme.accentColor,
        themeEmoji: theme.themeEmoji,
        symbols: theme.symbols.map(s => ({ id: s.id, name: s.name, emoji: s.emoji, type: s.type, value: s.value })),
        features: theme.features,
        tags: theme.tags,
      };
    }),

  /**
   * Get current jackpot amount for a game
   */
  getJackpot: publicProcedure
    .input(z.object({ gameId: z.number() }))
    .query(({ input }) => {
      const jackpot = getJackpot(input.gameId);
      return { amount: jackpot.getAmount() };
    }),

  /**
   * Get game statistics (plays, wins, average multiplier)
   */
  getGameStats: publicProcedure
    .input(z.object({ gameId: z.number() }))
    .query(async ({ input }) => {
      const game = await getGameById(input.gameId);
      if (!game) throw new Error("Game not found");

      // In production, query from game_sessions table
      return {
        gameId: input.gameId,
        playCount: game.playCount || 0,
        averageMultiplier: 1.5, // Placeholder
        winRate: 0.35, // 35% win rate typical
        totalWinAmount: 0, // Would sum from sessions
      };
    }),

  /**
   * Verify spin fairness (provably fair)
   */
  verifySpin: publicProcedure
    .input(z.object({
      gameId: z.number(),
      seed: z.string(),
      hash: z.string(),
      serverSeed: z.string(),
      clientSeed: z.string(),
    }))
    .query(({ input }) => {
      // In production, verify the hash matches SHA256(serverSeed:clientSeed)
      const crypto = require("crypto");
      const combined = `${input.serverSeed}:${input.clientSeed}`;
      const expectedHash = crypto.createHash("sha256").update(combined).digest("hex");

      return {
        isValid: input.hash === expectedHash,
        expectedHash,
        providedHash: input.hash,
      };
    }),
});
