import { EventEmitter } from 'events';
import SlotGameEngine, { SpinResult } from './slotGameEngine.js';

export interface WalletTransaction {
  id: string;
  userId: string;
  gameId: string;
  type: 'bet' | 'win' | 'bonus' | 'free_spin';
  amount: number;
  balanceBefore: number;
  balanceAfter: number;
  timestamp: Date;
  spinId?: string;
  status: 'pending' | 'completed' | 'failed' | 'reversed';
}

export interface PlayerWallet {
  userId: string;
  goldCoins: number;
  sweepsCoins: number;
  totalWagered: number;
  totalWon: number;
  totalBonusAwarded: number;
  lastUpdated: Date;
}

export interface SpinTransaction {
  spinId: string;
  userId: string;
  gameId: string;
  betAmount: number;
  winAmount: number;
  freeSpinsAwarded: number;
  transactions: WalletTransaction[];
  spinResult: SpinResult;
  status: 'completed' | 'failed' | 'reversed';
  timestamp: Date;
}

class SlotGameWalletService extends EventEmitter {
  private wallets: Map<string, PlayerWallet> = new Map();
  private transactions: Map<string, WalletTransaction[]> = new Map();
  private spinTransactions: Map<string, SpinTransaction> = new Map();

  constructor() {
    super();
  }

  /**
   * Initialize player wallet
   */
  initializeWallet(userId: string, initialGoldCoins: number = 1000, initialSweepsCoins: number = 500): PlayerWallet {
    const wallet: PlayerWallet = {
      userId,
      goldCoins: initialGoldCoins,
      sweepsCoins: initialSweepsCoins,
      totalWagered: 0,
      totalWon: 0,
      totalBonusAwarded: 0,
      lastUpdated: new Date(),
    };

    this.wallets.set(userId, wallet);
    this.transactions.set(userId, []);

    this.emit('wallet_initialized', wallet);

    return wallet;
  }

  /**
   * Get player wallet
   */
  getWallet(userId: string): PlayerWallet | null {
    return this.wallets.get(userId) || null;
  }

  /**
   * Process spin with wallet integration
   */
  async processSpin(
    userId: string,
    gameId: string,
    game: SlotGameEngine,
    betAmount: number,
    useSweepsCoins: boolean = true
  ): Promise<SpinTransaction> {
    const spinId = `spin-${Date.now()}`;
    const wallet = this.getWallet(userId);

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

    // Validate bet amount
    if (betAmount < 0.1 || betAmount > 100) {
      throw new Error('Invalid bet amount');
    }

    // Check balance
    const availableBalance = useSweepsCoins ? wallet.sweepsCoins : wallet.goldCoins;
    if (availableBalance < betAmount) {
      throw new Error('Insufficient balance');
    }

    const transactions: WalletTransaction[] = [];

    try {
      // BEGIN TRANSACTION
      // Step 1: Deduct bet
      const betTransaction = this.createTransaction(
        userId,
        gameId,
        'bet',
        -betAmount,
        wallet,
        useSweepsCoins,
        spinId
      );

      if (useSweepsCoins) {
        wallet.sweepsCoins -= betAmount;
      } else {
        wallet.goldCoins -= betAmount;
      }

      transactions.push(betTransaction);
      wallet.totalWagered += betAmount;

      // Step 2: Generate spin result
      const spinResult = game.spin(betAmount);

      // Step 3: Credit win if applicable
      if (spinResult.isWin && spinResult.winAmount > 0) {
        const winTransaction = this.createTransaction(
          userId,
          gameId,
          'win',
          spinResult.winAmount,
          wallet,
          useSweepsCoins,
          spinId
        );

        if (useSweepsCoins) {
          wallet.sweepsCoins += spinResult.winAmount;
        } else {
          wallet.goldCoins += spinResult.winAmount;
        }

        transactions.push(winTransaction);
        wallet.totalWon += spinResult.winAmount;
      }

      // Step 4: Award bonus if triggered
      if (spinResult.bonusTriggered && spinResult.freeSpinsAwarded > 0) {
        const bonusAmount = betAmount * spinResult.freeSpinsAwarded * 0.5; // 50% of bet per free spin
        const bonusTransaction = this.createTransaction(
          userId,
          gameId,
          'bonus',
          bonusAmount,
          wallet,
          useSweepsCoins,
          spinId
        );

        if (useSweepsCoins) {
          wallet.sweepsCoins += bonusAmount;
        } else {
          wallet.goldCoins += bonusAmount;
        }

        transactions.push(bonusTransaction);
        wallet.totalBonusAwarded += bonusAmount;
      }

      // Update wallet
      wallet.lastUpdated = new Date();
      this.wallets.set(userId, wallet);

      // Mark all transactions as completed
      transactions.forEach((t) => (t.status = 'completed'));

      // Store transactions
      const userTransactions = this.transactions.get(userId) || [];
      userTransactions.push(...transactions);
      this.transactions.set(userId, userTransactions);

      // Create spin transaction record
      const spinTransaction: SpinTransaction = {
        spinId,
        userId,
        gameId,
        betAmount,
        winAmount: spinResult.winAmount,
        freeSpinsAwarded: spinResult.freeSpinsAwarded,
        transactions,
        spinResult,
        status: 'completed',
        timestamp: new Date(),
      };

      this.spinTransactions.set(spinId, spinTransaction);

      // Update spin result with new balance
      spinResult.newBalance = useSweepsCoins ? wallet.sweepsCoins : wallet.goldCoins;

      this.emit('spin_completed', spinTransaction);

      return spinTransaction;
      // COMMIT
    } catch (error) {
      // ROLLBACK
      transactions.forEach((t) => (t.status = 'failed'));

      const failedSpinTransaction: SpinTransaction = {
        spinId,
        userId,
        gameId,
        betAmount,
        winAmount: 0,
        freeSpinsAwarded: 0,
        transactions,
        spinResult: {} as SpinResult,
        status: 'failed',
        timestamp: new Date(),
      };

      this.spinTransactions.set(spinId, failedSpinTransaction);
      this.emit('spin_failed', failedSpinTransaction);

      throw error;
    }
  }

  /**
   * Create wallet transaction
   */
  private createTransaction(
    userId: string,
    gameId: string,
    type: 'bet' | 'win' | 'bonus' | 'free_spin',
    amount: number,
    wallet: PlayerWallet,
    useSweepsCoins: boolean,
    spinId: string
  ): WalletTransaction {
    const balanceBefore = useSweepsCoins ? wallet.sweepsCoins : wallet.goldCoins;
    const balanceAfter = balanceBefore + amount;

    return {
      id: `txn-${Date.now()}`,
      userId,
      gameId,
      type,
      amount,
      balanceBefore,
      balanceAfter,
      timestamp: new Date(),
      spinId,
      status: 'pending',
    };
  }

  /**
   * Get player transactions
   */
  getPlayerTransactions(userId: string, limit: number = 100): WalletTransaction[] {
    const transactions = this.transactions.get(userId) || [];
    return transactions.slice(-limit);
  }

  /**
   * Get spin transaction
   */
  getSpinTransaction(spinId: string): SpinTransaction | null {
    return this.spinTransactions.get(spinId) || null;
  }

  /**
   * Get player spin history
   */
  getPlayerSpinHistory(userId: string, limit: number = 50): SpinTransaction[] {
    const allSpins = Array.from(this.spinTransactions.values());
    return allSpins.filter((s) => s.userId === userId).slice(-limit);
  }

  /**
   * Reverse transaction (admin only)
   */
  reverseTransaction(transactionId: string): WalletTransaction | null {
    for (const transactions of this.transactions.values()) {
      const transaction = transactions.find((t) => t.id === transactionId);
      if (transaction) {
        const wallet = this.getWallet(transaction.userId);
        if (wallet) {
          // Reverse the transaction
          const reverseAmount = -transaction.amount;
          wallet.sweepsCoins += reverseAmount;
          wallet.lastUpdated = new Date();
          this.wallets.set(transaction.userId, wallet);

          transaction.status = 'reversed';
          this.emit('transaction_reversed', transaction);

          return transaction;
        }
      }
    }
    return null;
  }

  /**
   * Add bonus to wallet (admin only)
   */
  addBonus(userId: string, amount: number, reason: string): WalletTransaction | null {
    const wallet = this.getWallet(userId);
    if (!wallet) {
      return null;
    }

    const transaction: WalletTransaction = {
      id: `bonus-${Date.now()}`,
      userId,
      gameId: 'admin',
      type: 'bonus',
      amount,
      balanceBefore: wallet.sweepsCoins,
      balanceAfter: wallet.sweepsCoins + amount,
      timestamp: new Date(),
      status: 'completed',
    };

    wallet.sweepsCoins += amount;
    wallet.totalBonusAwarded += amount;
    wallet.lastUpdated = new Date();

    this.wallets.set(userId, wallet);

    const userTransactions = this.transactions.get(userId) || [];
    userTransactions.push(transaction);
    this.transactions.set(userId, userTransactions);

    this.emit('bonus_added', { userId, amount, reason, transaction });

    return transaction;
  }

  /**
   * Get wallet statistics
   */
  getWalletStatistics(userId: string): {
    totalWagered: number;
    totalWon: number;
    totalBonusAwarded: number;
    netProfit: number;
    roi: number;
    spinCount: number;
    averageBet: number;
    averageWin: number;
    winRate: number;
  } | null {
    const wallet = this.getWallet(userId);
    if (!wallet) {
      return null;
    }

    const spinHistory = this.getPlayerSpinHistory(userId, 1000);
    const winCount = spinHistory.filter((s) => s.winAmount > 0).length;

    return {
      totalWagered: wallet.totalWagered,
      totalWon: wallet.totalWon,
      totalBonusAwarded: wallet.totalBonusAwarded,
      netProfit: wallet.totalWon - wallet.totalWagered,
      roi: wallet.totalWagered > 0 ? ((wallet.totalWon - wallet.totalWagered) / wallet.totalWagered) * 100 : 0,
      spinCount: spinHistory.length,
      averageBet: spinHistory.length > 0 ? wallet.totalWagered / spinHistory.length : 0,
      averageWin: winCount > 0 ? wallet.totalWon / winCount : 0,
      winRate: spinHistory.length > 0 ? (winCount / spinHistory.length) * 100 : 0,
    };
  }

  /**
   * Get all wallets (admin only)
   */
  getAllWallets(): PlayerWallet[] {
    return Array.from(this.wallets.values());
  }

  /**
   * Get total platform statistics
   */
  getPlatformStatistics(): {
    totalPlayers: number;
    totalWagered: number;
    totalWon: number;
    totalBonusAwarded: number;
    totalSpins: number;
    averageRTP: number;
  } {
    const wallets = Array.from(this.wallets.values());
    const allSpins = Array.from(this.spinTransactions.values());

    const totalWagered = wallets.reduce((sum, w) => sum + w.totalWagered, 0);
    const totalWon = wallets.reduce((sum, w) => sum + w.totalWon, 0);
    const totalBonusAwarded = wallets.reduce((sum, w) => sum + w.totalBonusAwarded, 0);

    return {
      totalPlayers: wallets.length,
      totalWagered,
      totalWon,
      totalBonusAwarded,
      totalSpins: allSpins.length,
      averageRTP: totalWagered > 0 ? (totalWon / totalWagered) * 100 : 0,
    };
  }

  /**
   * Export wallet data (admin only)
   */
  exportWalletData(userId: string): string {
    const wallet = this.getWallet(userId);
    const transactions = this.getPlayerTransactions(userId, 1000);
    const statistics = this.getWalletStatistics(userId);

    return JSON.stringify(
      {
        wallet,
        transactions,
        statistics,
      },
      null,
      2
    );
  }
}

export default SlotGameWalletService;
