import { getDb } from "../db.ts";
import { casinoGames, gameSessions } from "../../drizzle/schema.ts";
import { eq, and, sql, desc } from "drizzle-orm";

interface GameMetrics {
  gameId: number;
  gameName: string;
  currentRtp: number;
  actualRtp: number;
  totalSpins: number;
  totalWagered: number;
  totalWon: number;
  uniquePlayers: number;
  playerRetention: number; // percentage
  avgBetSize: number;
  volatility: string;
}

interface AdjustmentRecommendation {
  gameId: number;
  gameName: string;
  currentRtp: number;
  recommendedRtp: number;
  reason: string;
  impact: "increase" | "decrease" | "maintain";
  priority: "high" | "medium" | "low";
}

/**
 * Calculate actual RTP from game sessions
 */
export async function calculateActualRtp(gameId: number, days: number = 7): Promise<number> {
  const db = getDb();
  const daysAgo = new Date();
  daysAgo.setDate(daysAgo.getDate() - days);

  const analytics = await db
    .select({
      totalWagered: sql<number>`sum(cast(${gameSessions.betAmount} as decimal(15,2)))`,
      totalWon: sql<number>`sum(cast(${gameSessions.winAmount} as decimal(15,2)))`,
    })
    .from(gameSessions)
    .where(
      and(
        eq(gameSessions.gameId, gameId),
        sql`${gameSessions.createdAt} >= ${daysAgo.toISOString()}`
      )
    );

  const stats = analytics[0];
  if (!stats || !stats.totalWagered || stats.totalWagered === 0) {
    return 0;
  }

  return (stats.totalWon / stats.totalWagered) * 100;
}

/**
 * Get comprehensive game metrics
 */
export async function getGameMetrics(gameId: number, days: number = 7): Promise<GameMetrics | null> {
  const db = getDb();
  const daysAgo = new Date();
  daysAgo.setDate(daysAgo.getDate() - days);

  const game = await db
    .select()
    .from(casinoGames)
    .where(eq(casinoGames.id, gameId))
    .then((res) => res[0]);

  if (!game) return null;

  const analytics = await db
    .select({
      totalSpins: sql<number>`count(*)`,
      totalWagered: sql<number>`sum(cast(${gameSessions.betAmount} as decimal(15,2)))`,
      totalWon: sql<number>`sum(cast(${gameSessions.winAmount} as decimal(15,2)))`,
      uniquePlayers: sql<number>`count(distinct ${gameSessions.userId})`,
      avgBetSize: sql<number>`avg(cast(${gameSessions.betAmount} as decimal(10,2)))`,
    })
    .from(gameSessions)
    .where(
      and(
        eq(gameSessions.gameId, gameId),
        sql`${gameSessions.createdAt} >= ${daysAgo.toISOString()}`
      )
    );

  const stats = analytics[0] || {
    totalSpins: 0,
    totalWagered: 0,
    totalWon: 0,
    uniquePlayers: 0,
    avgBetSize: 0,
  };

  const actualRtp =
    stats.totalWagered && stats.totalWagered > 0
      ? (stats.totalWon / stats.totalWagered) * 100
      : 0;

  // Calculate player retention (simplified: unique players / sessions)
  const playerRetention =
    stats.totalSpins > 0 ? (stats.uniquePlayers / (stats.totalSpins / 10)) * 100 : 0;

  return {
    gameId,
    gameName: game.gameName,
    currentRtp: game.rtp,
    actualRtp: Math.round(actualRtp * 100) / 100,
    totalSpins: stats.totalSpins || 0,
    totalWagered: stats.totalWagered || 0,
    totalWon: stats.totalWon || 0,
    uniquePlayers: stats.uniquePlayers || 0,
    playerRetention: Math.min(100, Math.round(playerRetention * 100) / 100),
    avgBetSize: stats.avgBetSize || 0,
    volatility: game.volatility,
  };
}

/**
 * Generate RTP adjustment recommendations
 */
export async function generateRtpRecommendations(
  gameId: number,
  targetRetention: number = 70,
  targetRevenue: number = 1000
): Promise<AdjustmentRecommendation | null> {
  const metrics = await getGameMetrics(gameId, 7);
  if (!metrics) return null;

  const rtpDifference = Math.abs(metrics.currentRtp - metrics.actualRtp);
  const recommendations: AdjustmentRecommendation | null = null;

  // Rule 1: If actual RTP deviates significantly from configured RTP
  if (rtpDifference > 5) {
    const direction = metrics.actualRtp > metrics.currentRtp ? "decrease" : "increase";
    const adjustment = direction === "decrease" ? -1 : 1;

    return {
      gameId,
      gameName: metrics.gameName,
      currentRtp: metrics.currentRtp,
      recommendedRtp: Math.max(70, Math.min(99, metrics.currentRtp + adjustment)),
      reason: `Actual RTP (${metrics.actualRtp.toFixed(2)}%) deviates from configured RTP (${metrics.currentRtp}%)`,
      impact: direction === "decrease" ? "decrease" : "increase",
      priority: rtpDifference > 10 ? "high" : "medium",
    };
  }

  // Rule 2: If player retention is low, increase RTP to boost engagement
  if (metrics.playerRetention < targetRetention && metrics.currentRtp < 97) {
    return {
      gameId,
      gameName: metrics.gameName,
      currentRtp: metrics.currentRtp,
      recommendedRtp: Math.min(99, metrics.currentRtp + 1),
      reason: `Low player retention (${metrics.playerRetention.toFixed(2)}%). Increasing RTP may improve engagement.`,
      impact: "increase",
      priority: "medium",
    };
  }

  // Rule 3: If revenue is high and retention is good, slightly decrease RTP
  if (
    metrics.totalWagered > targetRevenue &&
    metrics.playerRetention > targetRetention + 10 &&
    metrics.currentRtp > 92
  ) {
    return {
      gameId,
      gameName: metrics.gameName,
      currentRtp: metrics.currentRtp,
      recommendedRtp: Math.max(92, metrics.currentRtp - 0.5),
      reason: `Strong performance (${metrics.playerRetention.toFixed(2)}% retention, $${metrics.totalWagered.toFixed(2)} wagered). Slight RTP decrease to optimize revenue.`,
      impact: "decrease",
      priority: "low",
    };
  }

  // Rule 4: If game has high volatility and low retention, increase RTP
  if (metrics.volatility === "high" && metrics.playerRetention < 60) {
    return {
      gameId,
      gameName: metrics.gameName,
      currentRtp: metrics.currentRtp,
      recommendedRtp: Math.min(99, metrics.currentRtp + 2),
      reason: `High volatility game with low retention. Increasing RTP to improve player experience.`,
      impact: "increase",
      priority: "high",
    };
  }

  return null;
}

/**
 * Apply RTP adjustment to a game
 */
export async function applyRtpAdjustment(
  gameId: number,
  newRtp: number,
  reason: string,
  adminId: number
): Promise<boolean> {
  if (newRtp < 70 || newRtp > 99) {
    throw new Error("RTP must be between 70% and 99%");
  }

  const db = getDb();

  // Update game RTP
  await db
    .update(casinoGames)
    .set({
      rtp: newRtp,
      updatedAt: new Date().toISOString(),
    })
    .where(eq(casinoGames.id, gameId));

  return true;
}

/**
 * Get all games needing RTP adjustment
 */
export async function getGamesNeedingAdjustment(
  limit: number = 10
): Promise<AdjustmentRecommendation[]> {
  const db = getDb();

  const games = await db
    .select({ id: casinoGames.id })
    .from(casinoGames)
    .where(eq(casinoGames.isActive, 1))
    .orderBy(desc(casinoGames.updatedAt))
    .limit(limit);

  const recommendations: AdjustmentRecommendation[] = [];

  for (const game of games) {
    const recommendation = await generateRtpRecommendations(game.id);
    if (recommendation) {
      recommendations.push(recommendation);
    }
  }

  // Sort by priority
  const priorityOrder = { high: 0, medium: 1, low: 2 };
  return recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
}

/**
 * Batch apply RTP adjustments based on metrics
 */
export async function batchApplyRtpAdjustments(
  adminId: number,
  dryRun: boolean = true
): Promise<{ applied: number; skipped: number; adjustments: AdjustmentRecommendation[] }> {
  const recommendations = await getGamesNeedingAdjustment(20);

  let applied = 0;
  let skipped = 0;

  for (const rec of recommendations) {
    if (!dryRun) {
      try {
        await applyRtpAdjustment(rec.gameId, rec.recommendedRtp, rec.reason, adminId);
        applied++;
      } catch (error) {
        console.error(`Failed to apply RTP adjustment for game ${rec.gameId}:`, error);
        skipped++;
      }
    }
  }

  return {
    applied: dryRun ? 0 : applied,
    skipped: dryRun ? 0 : skipped,
    adjustments: recommendations,
  };
}
