import { getDb } from "../db.ts";
import { gameSessions, users, transactions, wallets } from "../../drizzle/schema.ts";
import { eq, and, gte, lte } from "drizzle-orm";

export interface AnalyticsMetrics {
  dau: number; // Daily Active Users
  mau: number; // Monthly Active Users
  retention: {
    day1: number;
    day7: number;
    day30: number;
  };
  ltv: number; // Lifetime Value
  arpu: number; // Average Revenue Per User
  arppu: number; // Average Revenue Per Paying User
  totalRevenue: number;
  totalUsers: number;
  newUsersToday: number;
  avgSessionLength: number;
}

export interface UserCohort {
  cohortDate: Date;
  totalUsers: number;
  day1Retention: number;
  day7Retention: number;
  day30Retention: number;
}

export interface GameMetrics {
  gameId: number;
  gameName: string;
  totalPlays: number;
  totalWagered: number;
  totalWins: number;
  avgRTP: number;
  popularity: number;
}

export async function getDAU(date: Date = new Date()): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  const startOfDay = new Date(date);
  startOfDay.setHours(0, 0, 0, 0);

  const endOfDay = new Date(date);
  endOfDay.setHours(23, 59, 59, 999);

  const sessions = await db
    .select()
    .from(gameSessions)
    .where(and(gte(gameSessions.createdAt, startOfDay), lte(gameSessions.createdAt, endOfDay)));

  // Get unique users
  const uniqueUsers = new Set(sessions.map((s) => s.userId));
  return uniqueUsers.size;
}

export async function getMAU(date: Date = new Date()): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  const startOfMonth = new Date(date.getFullYear(), date.getMonth(), 1);
  const endOfMonth = new Date(date.getFullYear(), date.getMonth() + 1, 0);

  const sessions = await db
    .select()
    .from(gameSessions)
    .where(and(gte(gameSessions.createdAt, startOfMonth), lte(gameSessions.createdAt, endOfMonth)));

  // Get unique users
  const uniqueUsers = new Set(sessions.map((s) => s.userId));
  return uniqueUsers.size;
}

export async function getRetentionRates(cohortDate: Date): Promise<{
  day1: number;
  day7: number;
  day30: number;
}> {
  const db = await getDb();
  if (!db) return { day1: 0, day7: 0, day30: 0 };

  // Get users created on cohort date
  const cohortStartDate = new Date(cohortDate);
  cohortStartDate.setHours(0, 0, 0, 0);

  const cohortEndDate = new Date(cohortDate);
  cohortEndDate.setHours(23, 59, 59, 999);

  const cohortUsers = await db
    .select()
    .from(users)
    .where(and(gte(users.createdAt, cohortStartDate), lte(users.createdAt, cohortEndDate)));

  const cohortUserIds = new Set(cohortUsers.map((u) => u.id));

  if (cohortUserIds.size === 0) {
    return { day1: 0, day7: 0, day30: 0 };
  }

  // Check retention at day 1, 7, 30
  const day1Date = new Date(cohortDate);
  day1Date.setDate(day1Date.getDate() + 1);

  const day7Date = new Date(cohortDate);
  day7Date.setDate(day7Date.getDate() + 7);

  const day30Date = new Date(cohortDate);
  day30Date.setDate(day30Date.getDate() + 30);

  // Get sessions for each period
  const day1Sessions = await db
    .select()
    .from(gameSessions)
    .where(
      and(
        gte(gameSessions.createdAt, day1Date),
        lte(gameSessions.createdAt, new Date(day1Date.getTime() + 86400000))
      )
    );

  const day7Sessions = await db
    .select()
    .from(gameSessions)
    .where(
      and(
        gte(gameSessions.createdAt, day7Date),
        lte(gameSessions.createdAt, new Date(day7Date.getTime() + 86400000))
      )
    );

  const day30Sessions = await db
    .select()
    .from(gameSessions)
    .where(
      and(
        gte(gameSessions.createdAt, day30Date),
        lte(gameSessions.createdAt, new Date(day30Date.getTime() + 86400000))
      )
    );

  const day1RetainedUsers = new Set(day1Sessions.map((s) => s.userId).filter((id) => cohortUserIds.has(id)));
  const day7RetainedUsers = new Set(day7Sessions.map((s) => s.userId).filter((id) => cohortUserIds.has(id)));
  const day30RetainedUsers = new Set(day30Sessions.map((s) => s.userId).filter((id) => cohortUserIds.has(id)));

  return {
    day1: (day1RetainedUsers.size / cohortUserIds.size) * 100,
    day7: (day7RetainedUsers.size / cohortUserIds.size) * 100,
    day30: (day30RetainedUsers.size / cohortUserIds.size) * 100,
  };
}

export async function getARPU(): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  const allTransactions = await db.select().from(transactions);
  const completedTransactions = allTransactions.filter((t) => t.status === "completed" && t.type === "deposit");

  const totalRevenue = completedTransactions.reduce((sum, t) => sum + (t.amount || 0), 0);
  const totalUsers = await db.select().from(users);

  return totalUsers.length > 0 ? totalRevenue / totalUsers.length : 0;
}

export async function getARPPU(): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  const allTransactions = await db.select().from(transactions);
  const completedTransactions = allTransactions.filter((t) => t.status === "completed" && t.type === "deposit");

  const totalRevenue = completedTransactions.reduce((sum, t) => sum + (t.amount || 0), 0);

  // Get unique paying users
  const payingUsers = new Set(completedTransactions.map((t) => t.userId));

  return payingUsers.size > 0 ? totalRevenue / payingUsers.size : 0;
}

export async function getLTV(): Promise<number> {
  const db = await getDb();
  if (!db) return 0;

  const allTransactions = await db.select().from(transactions);
  const completedTransactions = allTransactions.filter((t) => t.status === "completed" && t.type === "deposit");

  const totalRevenue = completedTransactions.reduce((sum, t) => sum + (t.amount || 0), 0);
  const uniqueUsers = new Set(completedTransactions.map((t) => t.userId));

  return uniqueUsers.size > 0 ? totalRevenue / uniqueUsers.size : 0;
}

export async function getGameMetrics(): Promise<GameMetrics[]> {
  const db = await getDb();
  if (!db) return [];

  const allSessions = await db.select().from(gameSessions);

  // Group by game
  const gameData: Record<number, GameMetrics> = {};

  allSessions.forEach((session) => {
    const gameId = session.gameId || 0;

    if (!gameData[gameId]) {
      gameData[gameId] = {
        gameId,
        gameName: `Game ${gameId}`,
        totalPlays: 0,
        totalWagered: 0,
        totalWins: 0,
        avgRTP: 0,
        popularity: 0,
      };
    }

    gameData[gameId].totalPlays++;
    gameData[gameId].totalWagered += session.betAmount || 0;
    if (session.result === "win") {
      gameData[gameId].totalWins++;
    }
  });

  // Calculate metrics
  Object.values(gameData).forEach((game) => {
    game.avgRTP = game.totalWagered > 0 ? (game.totalWins / game.totalPlays) * 100 : 0;
    game.popularity = game.totalPlays;
  });

  return Object.values(gameData).sort((a, b) => b.popularity - a.popularity);
}

export async function getComprehensiveAnalytics(): Promise<AnalyticsMetrics> {
  const dau = await getDAU();
  const mau = await getMAU();
  const retention = await getRetentionRates(new Date(Date.now() - 30 * 24 * 60 * 60 * 1000));
  const ltv = await getLTV();
  const arpu = await getARPU();
  const arppu = await getARPPU();

  const db = await getDb();
  if (!db) {
    return {
      dau,
      mau,
      retention,
      ltv,
      arpu,
      arppu,
      totalRevenue: 0,
      totalUsers: 0,
      newUsersToday: 0,
      avgSessionLength: 0,
    };
  }

  const allTransactions = await db.select().from(transactions);
  const completedTransactions = allTransactions.filter((t) => t.status === "completed" && t.type === "deposit");
  const totalRevenue = completedTransactions.reduce((sum, t) => sum + (t.amount || 0), 0);

  const allUsers = await db.select().from(users);
  const totalUsers = allUsers.length;

  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const newUsersToday = allUsers.filter((u) => {
    const userDate = new Date(u.createdAt || new Date());
    userDate.setHours(0, 0, 0, 0);
    return userDate.getTime() === today.getTime();
  }).length;

  const allSessions = await db.select().from(gameSessions);
  const avgSessionLength = allSessions.length > 0 ? allSessions.reduce((sum, s) => sum + (s.duration || 0), 0) / allSessions.length : 0;

  return {
    dau,
    mau,
    retention,
    ltv,
    arpu,
    arppu,
    totalRevenue,
    totalUsers,
    newUsersToday,
    avgSessionLength,
  };
}
