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

export interface ABTestVariant {
  id: string;
  name: string;
  gameId: string;
  theme: string;
  rtp: number;
  volatility: string;
  description: string;
  allocation: number; // Percentage of traffic (0-100)
}

export interface ABTest {
  id: string;
  name: string;
  description: string;
  baseGameId: string;
  variants: ABTestVariant[];
  status: "draft" | "running" | "completed" | "paused";
  startDate: Date;
  endDate?: Date;
  createdAt: Date;
  createdBy: number;
}

export interface ABTestResults {
  testId: string;
  testName: string;
  variants: {
    variantId: string;
    variantName: string;
    plays: number;
    uniquePlayers: number;
    totalWinnings: number;
    totalBets: number;
    avgRTP: number;
    winRate: number;
    avgWinAmount: number;
    confidence: number; // Statistical confidence level
    winner: boolean;
  }[];
  statisticalSignificance: boolean;
  recommendedVariant?: string;
  conclusion: string;
}

/**
 * A/B Testing Framework for Game Variants
 */
export class ABTestingFramework {
  private static tests: Map<string, ABTest> = new Map();

  /**
   * Create a new A/B test
   */
  static createTest(
    name: string,
    description: string,
    baseGameId: string,
    variants: ABTestVariant[],
    userId: number
  ): ABTest {
    const testId = `test-${Date.now()}`;

    // Validate allocation adds up to 100
    const totalAllocation = variants.reduce((sum, v) => sum + v.allocation, 0);
    if (totalAllocation !== 100) {
      throw new Error("Variant allocations must sum to 100%");
    }

    const test: ABTest = {
      id: testId,
      name,
      description,
      baseGameId,
      variants,
      status: "draft",
      startDate: new Date(),
      createdAt: new Date(),
      createdBy: userId,
    };

    this.tests.set(testId, test);
    console.log(`✅ A/B test created: ${name} (Test ID: ${testId})`);

    return test;
  }

  /**
   * Start an A/B test
   */
  static startTest(testId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) return null;

    test.status = "running";
    test.startDate = new Date();
    this.tests.set(testId, test);

    console.log(`🚀 A/B test started: ${test.name}`);
    return test;
  }

  /**
   * Stop an A/B test
   */
  static stopTest(testId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) return null;

    test.status = "completed";
    test.endDate = new Date();
    this.tests.set(testId, test);

    console.log(`⏹️ A/B test stopped: ${test.name}`);
    return test;
  }

  /**
   * Get test results
   */
  static async getTestResults(testId: string): Promise<ABTestResults | null> {
    const test = this.tests.get(testId);
    if (!test) return null;

    try {
      const results: ABTestResults = {
        testId,
        testName: test.name,
        variants: [],
        statisticalSignificance: false,
        conclusion: "",
      };

      let maxWinRate = 0;
      let winnerVariantId = "";

      for (const variant of test.variants) {
        const sessions = await db
          .select()
          .from(gameSessions)
          .where(
            sql`game_id = ${variant.gameId} AND created_at >= ${test.startDate} ${
              test.endDate ? sql`AND created_at <= ${test.endDate}` : sql``
            }`
          );

        const plays = sessions.length;
        const uniquePlayers = new Set(sessions.map((s) => s.userId)).size;
        const totalWinnings = sessions.reduce((sum, s) => sum + (s.winAmount || 0), 0);
        const totalBets = sessions.reduce((sum, s) => sum + (s.betAmount || 0), 0);
        const avgRTP = totalBets > 0 ? (totalWinnings / totalBets) * 100 : 0;
        const winRate =
          (sessions.filter((s) => s.winAmount > 0).length / plays) * 100 || 0;
        const avgWinAmount =
          sessions.filter((s) => s.winAmount > 0).length > 0
            ? totalWinnings / sessions.filter((s) => s.winAmount > 0).length
            : 0;

        // Calculate statistical confidence (simplified)
        const confidence = Math.min(100, (plays / 100) * 100);

        const variantResult = {
          variantId: variant.id,
          variantName: variant.name,
          plays,
          uniquePlayers,
          totalWinnings,
          totalBets,
          avgRTP,
          winRate,
          avgWinAmount,
          confidence,
          winner: false,
        };

        if (winRate > maxWinRate) {
          maxWinRate = winRate;
          winnerVariantId = variant.id;
        }

        results.variants.push(variantResult);
      }

      // Mark winner
      const winnerVariant = results.variants.find((v) => v.variantId === winnerVariantId);
      if (winnerVariant) {
        winnerVariant.winner = true;
        results.recommendedVariant = winnerVariantId;
      }

      // Determine statistical significance
      const confidenceScores = results.variants.map((v) => v.confidence);
      results.statisticalSignificance = Math.min(...confidenceScores) > 80;

      // Generate conclusion
      if (results.statisticalSignificance) {
        const winner = results.variants.find((v) => v.winner);
        results.conclusion = `${winner?.variantName} is the clear winner with ${winner?.winRate.toFixed(2)}% win rate`;
      } else {
        results.conclusion = "Insufficient data for statistical significance. Continue test.";
      }

      return results;
    } catch (error) {
      console.error("Error getting test results:", error);
      return null;
    }
  }

  /**
   * Get variant for player based on test allocation
   */
  static getVariantForPlayer(testId: string): ABTestVariant | null {
    const test = this.tests.get(testId);
    if (!test || test.status !== "running") return null;

    const random = Math.random() * 100;
    let cumulativeAllocation = 0;

    for (const variant of test.variants) {
      cumulativeAllocation += variant.allocation;
      if (random < cumulativeAllocation) {
        return variant;
      }
    }

    return test.variants[0] || null;
  }

  /**
   * Get all tests
   */
  static getAllTests(): ABTest[] {
    return Array.from(this.tests.values());
  }

  /**
   * Get test by ID
   */
  static getTest(testId: string): ABTest | null {
    return this.tests.get(testId) || null;
  }

  /**
   * Update test allocation
   */
  static updateAllocation(
    testId: string,
    variantId: string,
    newAllocation: number
  ): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) return null;

    const variant = test.variants.find((v) => v.id === variantId);
    if (!variant) return null;

    // Calculate new allocation for other variants
    const oldAllocation = variant.allocation;
    const difference = newAllocation - oldAllocation;
    const otherVariants = test.variants.filter((v) => v.id !== variantId);

    if (otherVariants.length > 0) {
      const adjustmentPerVariant = difference / otherVariants.length;
      otherVariants.forEach((v) => {
        v.allocation -= adjustmentPerVariant;
      });
    }

    variant.allocation = newAllocation;
    this.tests.set(testId, test);

    console.log(`📊 Updated allocation for variant ${variantId}: ${newAllocation}%`);
    return test;
  }

  /**
   * Pause a test
   */
  static pauseTest(testId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) return null;

    test.status = "paused";
    this.tests.set(testId, test);

    console.log(`⏸️ A/B test paused: ${test.name}`);
    return test;
  }

  /**
   * Resume a test
   */
  static resumeTest(testId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) return null;

    test.status = "running";
    this.tests.set(testId, test);

    console.log(`▶️ A/B test resumed: ${test.name}`);
    return test;
  }

  /**
   * Get test statistics
   */
  static getStats() {
    const tests = Array.from(this.tests.values());
    const running = tests.filter((t) => t.status === "running").length;
    const completed = tests.filter((t) => t.status === "completed").length;
    const paused = tests.filter((t) => t.status === "paused").length;

    return {
      totalTests: tests.length,
      running,
      completed,
      paused,
      activeVariants: tests
        .filter((t) => t.status === "running")
        .reduce((sum, t) => sum + t.variants.length, 0),
    };
  }
}

export default ABTestingFramework;
