/**
 * Slot Game Engine - Wallet Integration
 * Atomic transaction handling for SC wallet balance management
 */

import { db } from '../db.ts';
import { wallets, users } from '@/drizzle/schema.ts';
import { eq } from 'drizzle-orm';
import type { SpinRequest, SpinResult } from './types';

export class WalletIntegration {
  /**
   * Process spin with atomic wallet transaction
   */
  static async processSpin(
    spinRequest: SpinRequest,
    spinResult: { winAmount: number; multiplier: number; reels: any[][] }
  ): Promise<SpinResult> {
    // Start transaction
    const transaction = db.transaction(async (tx) => {
      // 1. Get current wallet
      const wallet = await tx.query.wallets.findFirst({
        where: eq(wallets.userId, parseInt(spinRequest.userId)),
      });

      if (!wallet) {
        throw new Error('Wallet not found');
      }

      // 2. Check sufficient balance
      if (wallet.scBalance < spinRequest.betAmount) {
        throw new Error('Insufficient balance');
      }

      // 3. Deduct bet amount
      const balanceAfterBet = wallet.scBalance - spinRequest.betAmount;

      // 4. Calculate final win amount (with multiplier)
      const finalWinAmount = spinResult.winAmount * spinResult.multiplier;

      // 5. Credit winnings
      const finalBalance = balanceAfterBet + finalWinAmount;

      // 6. Update wallet
      await tx
        .update(wallets)
        .set({
          scBalance: finalBalance,
          totalWagered: wallet.totalWagered + spinRequest.betAmount,
          totalWon: wallet.totalWon + finalWinAmount,
          lastUpdated: new Date(),
        })
        .where(eq(wallets.userId, parseInt(spinRequest.userId)));

      // 7. Record transaction
      await this.recordTransaction(tx, {
        userId: parseInt(spinRequest.userId),
        gameId: spinRequest.gameId,
        betAmount: spinRequest.betAmount,
        winAmount: finalWinAmount,
        balanceBefore: wallet.scBalance,
        balanceAfter: finalBalance,
        type: finalWinAmount > 0 ? 'win' : 'loss',
      });

      return {
        reels: spinResult.reels,
        winAmount: finalWinAmount,
        newBalance: finalBalance,
        winLines: [],
        totalMultiplier: spinResult.multiplier,
        rtp: 96.0,
      };
    });

    return transaction;
  }

  /**
   * Process free spin (no balance deduction)
   */
  static async processFreeSpin(
    userId: string,
    gameId: string,
    spinResult: { winAmount: number; multiplier: number; reels: any[][] }
  ): Promise<SpinResult> {
    const transaction = db.transaction(async (tx) => {
      // Get wallet
      const wallet = await tx.query.wallets.findFirst({
        where: eq(wallets.userId, parseInt(userId)),
      });

      if (!wallet) {
        throw new Error('Wallet not found');
      }

      // Only credit winnings, no bet deduction
      const finalWinAmount = spinResult.winAmount * spinResult.multiplier;
      const finalBalance = wallet.scBalance + finalWinAmount;

      // Update wallet
      await tx
        .update(wallets)
        .set({
          scBalance: finalBalance,
          totalWon: wallet.totalWon + finalWinAmount,
          lastUpdated: new Date(),
        })
        .where(eq(wallets.userId, parseInt(userId)));

      // Record transaction
      await this.recordTransaction(tx, {
        userId: parseInt(userId),
        gameId,
        betAmount: 0,
        winAmount: finalWinAmount,
        balanceBefore: wallet.scBalance,
        balanceAfter: finalBalance,
        type: 'free_spin_win',
      });

      return {
        reels: spinResult.reels,
        winAmount: finalWinAmount,
        newBalance: finalBalance,
        winLines: [],
        totalMultiplier: spinResult.multiplier,
        rtp: 96.0,
      };
    });

    return transaction;
  }

  /**
   * Record game transaction for analytics
   */
  static async recordTransaction(
    tx: any,
    data: {
      userId: number;
      gameId: string;
      betAmount: number;
      winAmount: number;
      balanceBefore: number;
      balanceAfter: number;
      type: string;
    }
  ): Promise<void> {
    // This would insert into a game_transactions table
    // For now, we're tracking in the wallet
    console.log('[Transaction]', {
      userId: data.userId,
      gameId: data.gameId,
      bet: data.betAmount,
      win: data.winAmount,
      balanceBefore: data.balanceBefore,
      balanceAfter: data.balanceAfter,
      type: data.type,
      timestamp: new Date(),
    });
  }

  /**
   * Get player wallet balance
   */
  static async getBalance(userId: string): Promise<number> {
    const wallet = await db.query.wallets.findFirst({
      where: eq(wallets.userId, parseInt(userId)),
    });

    return wallet?.scBalance || 0;
  }

  /**
   * Add bonus to wallet
   */
  static async addBonus(userId: string, amount: number, reason: string): Promise<number> {
    const transaction = db.transaction(async (tx) => {
      const wallet = await tx.query.wallets.findFirst({
        where: eq(wallets.userId, parseInt(userId)),
      });

      if (!wallet) {
        throw new Error('Wallet not found');
      }

      const newBalance = wallet.scBalance + amount;

      await tx
        .update(wallets)
        .set({
          scBalance: newBalance,
          lastUpdated: new Date(),
        })
        .where(eq(wallets.userId, parseInt(userId)));

      console.log(`[Bonus] Added $${amount} to user ${userId}: ${reason}`);

      return newBalance;
    });

    return transaction;
  }

  /**
   * Verify transaction integrity
   */
  static async verifyTransaction(
    userId: string,
    expectedBalance: number
  ): Promise<{ valid: boolean; actualBalance: number }> {
    const wallet = await db.query.wallets.findFirst({
      where: eq(wallets.userId, parseInt(userId)),
    });

    const actualBalance = wallet?.scBalance || 0;
    const valid = Math.abs(actualBalance - expectedBalance) < 0.01; // Allow 1 cent variance

    return { valid, actualBalance };
  }
}
