import { db } from "../db.ts";
import { gameSessions, users } from "../../drizzle/schema.ts";
import { sql } from "drizzle-orm";

export interface PlayerLTV {
  userId: number;
  userName: string;
  totalSpent: number;
  totalWon: number;
  netRevenue: number;
  sessions: number;
  avgSessionValue: number;
  acquisitionSource: string;
  acquisitionGame: string;
  daysSinceAcquisition: number;
  churnRisk: "low" | "medium" | "high";
}

export interface GameRevenue {
  gameId: string;
  gameName: string;
  totalRevenue: number;
  totalPlayers: number;
  avgPlayerValue: number;
  revenuePerSession: number;
  conversionRate: number;
}

export interface AttributionMetrics {
  gameId: string;
  gameName: string;
  directRevenue: number; // Revenue from this game
  attributedRevenue: number; // Revenue from players acquired via this game
  totalImpact: number;
  roi: number;
  playerAcquisitionCost: number;
  paybackPeriod: number; // Days
}

/**
 * Revenue Attribution Model
 */
export class RevenueAttributionModel {
  /**
   * Calculate player lifetime value
   */
  static async calculatePlayerLTV(userId: number): Promise<PlayerLTV | null> {
    try {
      const sessions = await db
        .select()
        .from(gameSessions)
        .where(sql`user_id = ${userId}`);

      if (sessions.length === 0) {
        return null;
      }

      const user = await db
        .select()
        .from(users)
        .where(sql`id = ${userId}`)
        .limit(1);

      if (user.length === 0) {
        return null;
      }

      const totalSpent = sessions.reduce((sum, s) => sum + (s.betAmount || 0), 0);
      const totalWon = sessions.reduce((sum, s) => sum + (s.winAmount || 0), 0);
      const netRevenue = totalSpent - totalWon;
      const avgSessionValue = netRevenue / sessions.length;

      // Determine acquisition source (first game played)
      const firstSession = sessions.sort(
        (a, b) =>
          new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
      )[0];

      const daysSinceAcquisition = Math.floor(
        (Date.now() - new Date(firstSession.createdAt).getTime()) /
          (1000 * 60 * 60 * 24)
      );

      // Determine churn risk
      const lastPlayDate = new Date(
        sessions.sort(
          (a, b) =>
            new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
        )[0].createdAt
      );
      const daysSinceLastPlay = Math.floor(
        (Date.now() - lastPlayDate.getTime()) / (1000 * 60 * 60 * 24)
      );

      let churnRisk: "low" | "medium" | "high" = "low";
      if (daysSinceLastPlay > 30) churnRisk = "high";
      else if (daysSinceLastPlay > 14) churnRisk = "medium";

      return {
        userId,
        userName: user[0].email || `User ${userId}`,
        totalSpent,
        totalWon,
        netRevenue,
        sessions: sessions.length,
        avgSessionValue,
        acquisitionSource: "organic", // Can be extended to track actual source
        acquisitionGame: firstSession.gameId,
        daysSinceAcquisition,
        churnRisk,
      };
    } catch (error) {
      console.error("Error calculating player LTV:", error);
      return null;
    }
  }

  /**
   * Get game revenue metrics
   */
  static async getGameRevenue(gameId: string): Promise<GameRevenue | null> {
    try {
      const sessions = await db
        .select()
        .from(gameSessions)
        .where(sql`game_id = ${gameId}`);

      if (sessions.length === 0) {
        return null;
      }

      const totalRevenue = sessions.reduce(
        (sum, s) => sum + (s.betAmount - s.winAmount || 0),
        0
      );
      const uniquePlayers = new Set(sessions.map((s) => s.userId)).size;
      const avgPlayerValue = totalRevenue / uniquePlayers;
      const revenuePerSession = totalRevenue / sessions.length;

      // Get game info for name
      const game = await db
        .select()
        .from(sql`all_games`)
        .where(sql`game_id = ${gameId}`)
        .limit(1);

      return {
        gameId,
        gameName: game[0]?.name || "Unknown",
        totalRevenue,
        totalPlayers: uniquePlayers,
        avgPlayerValue,
        revenuePerSession,
        conversionRate: (uniquePlayers / sessions.length) * 100,
      };
    } catch (error) {
      console.error("Error getting game revenue:", error);
      return null;
    }
  }

  /**
   * Calculate attribution metrics for a game
   */
  static async calculateAttributionMetrics(
    gameId: string
  ): Promise<AttributionMetrics | null> {
    try {
      const gameRevenue = await this.getGameRevenue(gameId);
      if (!gameRevenue) return null;

      // Get players who started with this game
      const sessions = await db
        .select()
        .from(gameSessions)
        .where(sql`game_id = ${gameId}`);

      let attributedRevenue = 0;
      const playerIds = new Set(sessions.map((s) => s.userId));

      // Calculate revenue from players acquired via this game
      for (const userId of playerIds) {
        const allPlayerSessions = await db
          .select()
          .from(gameSessions)
          .where(sql`user_id = ${userId}`);

        const playerRevenue = allPlayerSessions.reduce(
          (sum, s) => sum + (s.betAmount - s.winAmount || 0),
          0
        );

        attributedRevenue += playerRevenue;
      }

      const totalImpact = gameRevenue.totalRevenue + attributedRevenue;
      const roi = (totalImpact / (gameRevenue.totalPlayers * 10)) * 100; // Assuming $10 CAC
      const paybackPeriod = Math.ceil(
        gameRevenue.totalPlayers * 10 / (gameRevenue.revenuePerSession || 1)
      );

      return {
        gameId,
        gameName: gameRevenue.gameName,
        directRevenue: gameRevenue.totalRevenue,
        attributedRevenue,
        totalImpact,
        roi,
        playerAcquisitionCost: 10, // Placeholder
        paybackPeriod,
      };
    } catch (error) {
      console.error("Error calculating attribution metrics:", error);
      return null;
    }
  }

  /**
   * Get top revenue-generating games
   */
  static async getTopRevenueGames(limit: number = 10): Promise<GameRevenue[]> {
    try {
      const games = await db.select().from(sql`all_games`);

      const revenues: GameRevenue[] = [];

      for (const game of games) {
        const revenue = await this.getGameRevenue(game.game_id);
        if (revenue && revenue.totalRevenue > 0) {
          revenues.push(revenue);
        }
      }

      return revenues
        .sort((a, b) => b.totalRevenue - a.totalRevenue)
        .slice(0, limit);
    } catch (error) {
      console.error("Error getting top revenue games:", error);
      return [];
    }
  }

  /**
   * Get high-value player cohorts
   */
  static async getHighValuePlayerCohorts(): Promise<any[]> {
    try {
      const allUsers = await db.select().from(users);

      const cohorts: Map<string, any> = new Map();

      for (const user of allUsers) {
        const ltv = await this.calculatePlayerLTV(user.id);
        if (ltv && ltv.netRevenue > 100) {
          // High-value threshold
          const cohortKey = `${ltv.acquisitionGame}-${ltv.daysSinceAcquisition > 30 ? "long-term" : "short-term"}`;

          if (!cohorts.has(cohortKey)) {
            cohorts.set(cohortKey, {
              acquisitionGame: ltv.acquisitionGame,
              timeframe: ltv.daysSinceAcquisition > 30 ? "long-term" : "short-term",
              players: [],
              totalRevenue: 0,
              avgLTV: 0,
            });
          }

          const cohort = cohorts.get(cohortKey)!;
          cohort.players.push(ltv);
          cohort.totalRevenue += ltv.netRevenue;
          cohort.avgLTV = cohort.totalRevenue / cohort.players.length;
        }
      }

      return Array.from(cohorts.values()).sort(
        (a, b) => b.totalRevenue - a.totalRevenue
      );
    } catch (error) {
      console.error("Error getting high-value player cohorts:", error);
      return [];
    }
  }

  /**
   * Forecast revenue impact of acquisition channel
   */
  static async forecastRevenueImpact(
    gameId: string,
    acquisitionVolume: number
  ): Promise<any> {
    try {
      const attribution = await this.calculateAttributionMetrics(gameId);
      if (!attribution) return null;

      const avgPlayerValue = attribution.directRevenue / 100; // Estimate
      const projectedRevenue = acquisitionVolume * avgPlayerValue;
      const projectedRoi = (projectedRevenue / (acquisitionVolume * 10)) * 100;

      return {
        gameId,
        acquisitionVolume,
        avgPlayerValue,
        projectedRevenue,
        projectedRoi,
        paybackPeriod: attribution.paybackPeriod,
        recommendation:
          projectedRoi > 300
            ? "Highly recommended - strong ROI"
            : projectedRoi > 100
            ? "Recommended - good ROI"
            : "Consider optimizing - low ROI",
      };
    } catch (error) {
      console.error("Error forecasting revenue impact:", error);
      return null;
    }
  }

  /**
   * Get revenue attribution dashboard
   */
  static async getAttributionDashboard(): Promise<any> {
    try {
      const topGames = await this.getTopRevenueGames(5);
      const highValueCohorts = await this.getHighValuePlayerCohorts();

      const attributionMetrics: AttributionMetrics[] = [];
      for (const game of topGames) {
        const metrics = await this.calculateAttributionMetrics(game.gameId);
        if (metrics) {
          attributionMetrics.push(metrics);
        }
      }

      return {
        timestamp: new Date(),
        topGames,
        highValueCohorts,
        attributionMetrics,
        totalRevenue: topGames.reduce((sum, g) => sum + g.totalRevenue, 0),
        avgPlayerValue: topGames.reduce((sum, g) => sum + g.avgPlayerValue, 0) / topGames.length,
      };
    } catch (error) {
      console.error("Error getting attribution dashboard:", error);
      return null;
    }
  }
}

export default RevenueAttributionModel;
