/**
 * Game Leaderboards System
 * Real-time leaderboards for each game with daily/weekly/monthly/alltime periods
 */

import { eq, desc, and, gte } from 'drizzle-orm';
import { getDb } from './db.ts';
import { transactions, users } from '../drizzle/schema.ts';

export type LeaderboardPeriod = 'daily' | 'weekly' | 'monthly' | 'alltime';

export interface LeaderboardEntry {
  rank: number;
  userId: number;
  userName: string;
  gameId: string;
  totalWinnings: number;
  totalSpins: number;
  winRate: number;
  biggestWin: number;
  averageWin: number;
}

export interface GameLeaderboard {
  gameId: string;
  period: LeaderboardPeriod;
  entries: LeaderboardEntry[];
  generatedAt: Date;
}

/**
 * Get time range for leaderboard period
 */
function getTimeRange(period: LeaderboardPeriod): Date {
  const now = new Date();
  switch (period) {
    case 'daily':
      return new Date(now.getTime() - 24 * 60 * 60 * 1000);
    case 'weekly':
      return new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
    case 'monthly':
      return new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
    case 'alltime':
      return new Date(0);
  }
}

/**
 * Get game leaderboard for a specific period
 */
export async function getGameLeaderboard(
  gameId: string,
  period: LeaderboardPeriod,
  limit: number = 100
): Promise<LeaderboardEntry[]> {
  const db = getDb();
  const timeRange = getTimeRange(period);

  // Get all transactions for this game in the period
  const gameTransactions = await db.query.transactions.findMany({
    where: (t) =>
      and(
        eq(t.gameId, gameId),
        gte(t.createdAt, timeRange)
      ),
  });

  // Group by user and calculate stats
  const userStats: Record<
    number,
    {
      userId: number;
      totalWinnings: number;
      totalSpins: number;
      totalWagered: number;
      wins: number;
      biggestWin: number;
    }
  > = {};

  for (const tx of gameTransactions) {
    if (!userStats[tx.userId]) {
      userStats[tx.userId] = {
        userId: tx.userId,
        totalWinnings: 0,
        totalSpins: 0,
        totalWagered: 0,
        wins: 0,
        biggestWin: 0,
      };
    }

    userStats[tx.userId].totalSpins++;
    userStats[tx.userId].totalWagered += tx.amount || 0;
    userStats[tx.userId].totalWinnings += tx.winAmount || 0;

    if (tx.winAmount && tx.winAmount > 0) {
      userStats[tx.userId].wins++;
      userStats[tx.userId].biggestWin = Math.max(
        userStats[tx.userId].biggestWin,
        tx.winAmount
      );
    }
  }

  // Get user names
  const userIds = Object.keys(userStats).map(Number);
  const userRecords = await db.query.users.findMany({
    where: (u) => userIds.includes(u.id),
  });

  const userNameMap = new Map(userRecords.map((u) => [u.id, u.name]));

  // Sort by winnings and create leaderboard entries
  const entries: LeaderboardEntry[] = Object.values(userStats)
    .sort((a, b) => b.totalWinnings - a.totalWinnings)
    .slice(0, limit)
    .map((stat, index) => ({
      rank: index + 1,
      userId: stat.userId,
      userName: userNameMap.get(stat.userId) || 'Anonymous',
      gameId,
      totalWinnings: stat.totalWinnings,
      totalSpins: stat.totalSpins,
      winRate: stat.totalSpins > 0 ? (stat.wins / stat.totalSpins) * 100 : 0,
      biggestWin: stat.biggestWin,
      averageWin: stat.wins > 0 ? stat.totalWinnings / stat.wins : 0,
    }));

  return entries;
}

/**
 * Get player rank on leaderboard
 */
export async function getPlayerRank(
  userId: number,
  gameId: string,
  period: LeaderboardPeriod
): Promise<{ rank: number; entry: LeaderboardEntry | null }> {
  const leaderboard = await getGameLeaderboard(gameId, period, 10000);
  const entry = leaderboard.find((e) => e.userId === userId);

  return {
    rank: entry?.rank || 0,
    entry: entry || null,
  };
}

/**
 * Get all game leaderboards
 */
export async function getAllGameLeaderboards(
  period: LeaderboardPeriod,
  limit: number = 100
): Promise<GameLeaderboard[]> {
  const games = ['neon-nights', 'dragons-fury', 'cosmic-quest', 'retro-arcade'];

  const leaderboards: GameLeaderboard[] = [];

  for (const gameId of games) {
    const entries = await getGameLeaderboard(gameId, period, limit);
    leaderboards.push({
      gameId,
      period,
      entries,
      generatedAt: new Date(),
    });
  }

  return leaderboards;
}

/**
 * Get top winners across all games
 */
export async function getTopWinnersAllGames(
  period: LeaderboardPeriod,
  limit: number = 100
): Promise<LeaderboardEntry[]> {
  const db = getDb();
  const timeRange = getTimeRange(period);

  // Get all transactions in the period
  const allTransactions = await db.query.transactions.findMany({
    where: (t) => gte(t.createdAt, timeRange),
  });

  // Group by user
  const userStats: Record<
    number,
    {
      userId: number;
      totalWinnings: number;
      totalSpins: number;
      wins: number;
      biggestWin: number;
    }
  > = {};

  for (const tx of allTransactions) {
    if (!userStats[tx.userId]) {
      userStats[tx.userId] = {
        userId: tx.userId,
        totalWinnings: 0,
        totalSpins: 0,
        wins: 0,
        biggestWin: 0,
      };
    }

    userStats[tx.userId].totalSpins++;
    userStats[tx.userId].totalWinnings += tx.winAmount || 0;

    if (tx.winAmount && tx.winAmount > 0) {
      userStats[tx.userId].wins++;
      userStats[tx.userId].biggestWin = Math.max(
        userStats[tx.userId].biggestWin,
        tx.winAmount
      );
    }
  }

  // Get user names
  const userIds = Object.keys(userStats).map(Number);
  const userRecords = await db.query.users.findMany({
    where: (u) => userIds.includes(u.id),
  });

  const userNameMap = new Map(userRecords.map((u) => [u.id, u.name]));

  // Sort and create entries
  const entries: LeaderboardEntry[] = Object.values(userStats)
    .sort((a, b) => b.totalWinnings - a.totalWinnings)
    .slice(0, limit)
    .map((stat, index) => ({
      rank: index + 1,
      userId: stat.userId,
      userName: userNameMap.get(stat.userId) || 'Anonymous',
      gameId: 'all-games',
      totalWinnings: stat.totalWinnings,
      totalSpins: stat.totalSpins,
      winRate: stat.totalSpins > 0 ? (stat.wins / stat.totalSpins) * 100 : 0,
      biggestWin: stat.biggestWin,
      averageWin: stat.wins > 0 ? stat.totalWinnings / stat.wins : 0,
    }));

  return entries;
}

/**
 * Get leaderboard stats for a game
 */
export async function getLeaderboardStats(gameId: string, period: LeaderboardPeriod) {
  const leaderboard = await getGameLeaderboard(gameId, period, 100);

  if (leaderboard.length === 0) {
    return {
      totalPlayers: 0,
      totalWinnings: 0,
      averageWinnings: 0,
      topWinner: null,
    };
  }

  const totalWinnings = leaderboard.reduce((sum, e) => sum + e.totalWinnings, 0);

  return {
    totalPlayers: leaderboard.length,
    totalWinnings,
    averageWinnings: totalWinnings / leaderboard.length,
    topWinner: leaderboard[0],
  };
}
