import { z } from "zod";
import { router, protectedProcedure } from "../_core/trpc.ts";
import { creditWallet, debitWallet, writeAuditLog, getOrCreateWallet, getDb } from "../db.ts";
import { eq } from "drizzle-orm";
import { transactions } from "../../drizzle/schema.ts";
import { createNotification } from "./notifications.ts";
import { detectFraud, createFraudAlert } from "../services/fraudDetection.ts";

export const gameConfigSlotsRouter = router({
  /**
   * Spin a game from GAME_CONFIGS
   * Handles balance deduction, win calculation, and balance credit
   */
  spin: protectedProcedure
    .input(
      z.object({
        gameId: z.string(), // e.g., "golden-pharaoh"
        betAmount: z.number().min(0.01).max(1000),
        gameMode: z.enum(["fun", "real"]), // fun = GC, real = SC
      })
    )
    .mutation(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      // Get user wallet
      const wallet = await getOrCreateWallet(ctx.user.id);
      const currency = input.gameMode === "fun" ? "GC" : "SC";
      const currentBalance = parseFloat(
        currency === "GC" ? wallet.gcBalance : wallet.scBalance
      );

      // Check sufficient balance
      if (currentBalance < input.betAmount) {
        throw new Error(`Insufficient ${currency} balance`);
      }

      // Run fraud detection
      const fraudResult = await detectFraud(ctx.user.id, input.betAmount);
      if (fraudResult.isFraudulent) {
        // Log fraud alerts to database
        for (const alert of fraudResult.alerts) {
          await createFraudAlert(
            ctx.user.id,
            alert.type,
            alert.severity,
            alert.description,
            alert.details
          );
        }
        // Still allow the spin but flag for review
        console.warn(`[Fraud Detection] User ${ctx.user.id} flagged with risk score ${fraudResult.riskScore}`);
      }

      // Deduct bet
      await debitWallet(
        ctx.user.id,
        currency,
        input.betAmount,
        "game_bet",
        `Bet on ${input.gameId}`,
        input.gameId,
        "game_config"
      );

      // Simulate spin result (simplified win calculation)
      // In production, this would use a proper RNG with server seed
      const winAmount = calculateWin(input.betAmount);

      // Credit win if any
      let finalBalance = currentBalance - input.betAmount;
      if (winAmount > 0) {
        finalBalance = await creditWallet(
          ctx.user.id,
          currency,
          winAmount,
          "game_win",
          `Won ${winAmount.toFixed(2)} ${currency} on ${input.gameId}`,
          input.gameId,
          "game_config"
        );
      }

      // Log audit
      await writeAuditLog({
        actorId: ctx.user.id,
        actorRole: "user",
        action: "game_spin",
        category: "game",
        details: {
          gameId: input.gameId,
          gameMode: input.gameMode,
          betAmount: input.betAmount,
          winAmount,
          currency,
        },
      });

      // Create notification for win/loss
      if (winAmount > 0) {
        const multiplier = (winAmount / input.betAmount).toFixed(1);
        const isLargeWin = winAmount > input.betAmount * 5;
        await createNotification(
          ctx.user.id,
          isLargeWin ? "big_win" : "game_win",
          isLargeWin ? "🎉 BIG WIN!" : "✨ You Won!",
          `Won ${winAmount.toFixed(2)} ${currency} on ${input.gameId}! (${multiplier}x multiplier)`,
          ["in_app"],
          {
            gameId: input.gameId,
            winAmount,
            multiplier,
            currency,
            isBigWin: isLargeWin,
          }
        );
      } else {
        await createNotification(
          ctx.user.id,
          "game_loss",
          "No Win",
          `Better luck next time! Lost ${input.betAmount.toFixed(2)} ${currency} on ${input.gameId}`,
          ["in_app"],
          {
            gameId: input.gameId,
            betAmount: input.betAmount,
            currency,
          }
        );
      }

      return {
        success: true,
        betAmount: input.betAmount,
        winAmount,
        finalBalance,
        currency,
        gameId: input.gameId,
        timestamp: new Date().toISOString(),
      };
    }),

  /**
   * Get game history for a user
   */
  getGameHistory: protectedProcedure
    .input(
      z.object({
        gameId: z.string().optional(),
        limit: z.number().min(1).max(100).default(20),
        offset: z.number().min(0).default(0),
      })
    )
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      let query = db
        .select()
        .from(transactions)
        .where(eq(transactions.userId, ctx.user.id))
        .$dynamic();

      if (input.gameId) {
        query = query.where(eq(transactions.referenceId, input.gameId));
      }

      const history = await query
        .orderBy(transactions.createdAt)
        .limit(input.limit)
        .offset(input.offset);

      return history.map((tx) => ({
        id: tx.id,
        type: tx.type,
        currency: tx.currency,
        amount: parseFloat(tx.amount),
        balanceBefore: parseFloat(tx.balanceBefore),
        balanceAfter: parseFloat(tx.balanceAfter),
        description: tx.description,
        createdAt: tx.createdAt,
      }));
    }),

  /**
   * Get game statistics
   */
  getGameStats: protectedProcedure
    .input(z.object({ gameId: z.string() }))
    .query(async ({ ctx, input }) => {
      const db = await getDb();
      if (!db) throw new Error("Database unavailable");

      const history = await db
        .select()
        .from(transactions)
        .where(eq(transactions.userId, ctx.user.id));

      const gameHistory = history.filter(
        (tx) => tx.referenceId === input.gameId
      );

      const totalBet = gameHistory
        .filter((tx) => tx.type === "game_spin_bet")
        .reduce((sum, tx) => sum + parseFloat(tx.amount), 0);

      const totalWin = gameHistory
        .filter((tx) => tx.type === "game_spin_win")
        .reduce((sum, tx) => sum + parseFloat(tx.amount), 0);

      const totalSpins = gameHistory.filter(
        (tx) => tx.type === "game_spin_bet"
      ).length;

      return {
        gameId: input.gameId,
        totalSpins,
        totalBet: totalBet.toFixed(2),
        totalWin: totalWin.toFixed(2),
        netResult: (totalWin - totalBet).toFixed(2),
        rtp: totalSpins > 0 ? ((totalWin / totalBet) * 100).toFixed(2) : "0.00",
      };
    }),
});

/**
 * Simple win calculation
 * In production, use proper RNG with server seed
 */
function calculateWin(betAmount: number): number {
  // 70% of spins are losses
  if (Math.random() < 0.7) return 0;

  // 20% of spins are small wins (1-3x bet)
  if (Math.random() < 0.2 / 0.3) {
    const multiplier = 1 + Math.random() * 2;
    return betAmount * multiplier;
  }

  // 10% of spins are big wins (3-10x bet)
  const multiplier = 3 + Math.random() * 7;
  return betAmount * multiplier;
}
