import { EventEmitter } from 'events';

export interface ABTestVariant {
  id: string;
  campaignId: string;
  variantName: string;
  title: string;
  message: string;
  imageUrl?: string;
  ctaText?: string;
  ctaUrl?: string;
  splitPercentage: number;
  sentCount: number;
  deliveredCount: number;
  openedCount: number;
  clickedCount: number;
  createdAt: Date;
}

export interface ABTest {
  id: string;
  campaignId: string;
  name: string;
  description?: string;
  variants: ABTestVariant[];
  status: 'draft' | 'running' | 'paused' | 'completed' | 'cancelled';
  startDate?: Date;
  endDate?: Date;
  winnerVariantId?: string;
  createdAt: Date;
  updatedAt: Date;
}

export interface ABTestResult {
  testId: string;
  variantId: string;
  variantName: string;
  sentCount: number;
  deliveredCount: number;
  openedCount: number;
  clickedCount: number;
  deliveryRate: number;
  openRate: number;
  clickRate: number;
  conversionRate: number;
}

class ABTestingService extends EventEmitter {
  private tests: Map<string, ABTest> = new Map();
  private variantMetrics: Map<string, ABTestVariant> = new Map();

  constructor() {
    super();
  }

  /**
   * Create a new A/B test
   */
  createTest(
    campaignId: string,
    name: string,
    variants: Array<{
      variantName: string;
      title: string;
      message: string;
      imageUrl?: string;
      ctaText?: string;
      ctaUrl?: string;
      splitPercentage: number;
    }>,
    description?: string
  ): ABTest {
    // Validate split percentages add up to 100
    const totalPercentage = variants.reduce((sum, v) => sum + v.splitPercentage, 0);
    if (totalPercentage !== 100) {
      throw new Error('Split percentages must add up to 100%');
    }

    const testId = `abtest-${Date.now()}`;
    const createdVariants: ABTestVariant[] = variants.map((v, index) => ({
      id: `variant-${testId}-${index}`,
      campaignId,
      variantName: v.variantName,
      title: v.title,
      message: v.message,
      imageUrl: v.imageUrl,
      ctaText: v.ctaText,
      ctaUrl: v.ctaUrl,
      splitPercentage: v.splitPercentage,
      sentCount: 0,
      deliveredCount: 0,
      openedCount: 0,
      clickedCount: 0,
      createdAt: new Date(),
    }));

    const test: ABTest = {
      id: testId,
      campaignId,
      name,
      description,
      variants: createdVariants,
      status: 'draft',
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    this.tests.set(testId, test);

    // Store variants for quick access
    for (const variant of createdVariants) {
      this.variantMetrics.set(variant.id, variant);
    }

    this.emit('test_created', test);

    return test;
  }

  /**
   * Start A/B test
   */
  startTest(testId: string, startDate?: Date): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) {
      return null;
    }

    if (test.status !== 'draft') {
      throw new Error('Only draft tests can be started');
    }

    test.status = 'running';
    test.startDate = startDate || new Date();
    test.updatedAt = new Date();

    this.emit('test_started', test);

    return test;
  }

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

    if (test.status !== 'running') {
      throw new Error('Only running tests can be paused');
    }

    test.status = 'paused';
    test.updatedAt = new Date();

    this.emit('test_paused', test);

    return test;
  }

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

    if (test.status !== 'paused') {
      throw new Error('Only paused tests can be resumed');
    }

    test.status = 'running';
    test.updatedAt = new Date();

    this.emit('test_resumed', test);

    return test;
  }

  /**
   * Complete A/B test and declare winner
   */
  completeTest(testId: string, winnerVariantId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) {
      return null;
    }

    const winnerExists = test.variants.some((v) => v.id === winnerVariantId);
    if (!winnerExists) {
      throw new Error('Winner variant not found in test');
    }

    test.status = 'completed';
    test.winnerVariantId = winnerVariantId;
    test.endDate = new Date();
    test.updatedAt = new Date();

    this.emit('test_completed', test);

    return test;
  }

  /**
   * Cancel A/B test
   */
  cancelTest(testId: string): ABTest | null {
    const test = this.tests.get(testId);
    if (!test) {
      return null;
    }

    test.status = 'cancelled';
    test.endDate = new Date();
    test.updatedAt = new Date();

    this.emit('test_cancelled', test);

    return test;
  }

  /**
   * Track variant event
   */
  trackVariantEvent(
    variantId: string,
    eventType: 'sent' | 'delivered' | 'opened' | 'clicked'
  ): boolean {
    const variant = this.variantMetrics.get(variantId);
    if (!variant) {
      return false;
    }

    switch (eventType) {
      case 'sent':
        variant.sentCount++;
        break;
      case 'delivered':
        variant.deliveredCount++;
        break;
      case 'opened':
        variant.openedCount++;
        break;
      case 'clicked':
        variant.clickedCount++;
        break;
    }

    this.emit('variant_event_tracked', { variantId, eventType });

    return true;
  }

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

  /**
   * Get all tests
   */
  getAllTests(): ABTest[] {
    return Array.from(this.tests.values()).sort(
      (a, b) => b.createdAt.getTime() - a.createdAt.getTime()
    );
  }

  /**
   * Get tests by campaign
   */
  getTestsByCampaign(campaignId: string): ABTest[] {
    return Array.from(this.tests.values())
      .filter((t) => t.campaignId === campaignId)
      .sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
  }

  /**
   * Get test results
   */
  getTestResults(testId: string): ABTestResult[] {
    const test = this.tests.get(testId);
    if (!test) {
      return [];
    }

    return test.variants.map((variant) => ({
      testId,
      variantId: variant.id,
      variantName: variant.variantName,
      sentCount: variant.sentCount,
      deliveredCount: variant.deliveredCount,
      openedCount: variant.openedCount,
      clickedCount: variant.clickedCount,
      deliveryRate: variant.sentCount > 0 ? (variant.deliveredCount / variant.sentCount) * 100 : 0,
      openRate: variant.sentCount > 0 ? (variant.openedCount / variant.sentCount) * 100 : 0,
      clickRate: variant.sentCount > 0 ? (variant.clickedCount / variant.sentCount) * 100 : 0,
      conversionRate: variant.clickedCount,
    }));
  }

  /**
   * Get winner recommendation
   */
  getWinnerRecommendation(testId: string): ABTestResult | null {
    const results = this.getTestResults(testId);
    if (results.length === 0) {
      return null;
    }

    // Recommend based on open rate (can be customized)
    return results.reduce((best, current) =>
      current.openRate > best.openRate ? current : best
    );
  }

  /**
   * Get statistical significance
   */
  getStatisticalSignificance(testId: string): {
    isSignificant: boolean;
    confidence: number;
    recommendation: string;
  } {
    const results = this.getTestResults(testId);
    if (results.length < 2) {
      return {
        isSignificant: false,
        confidence: 0,
        recommendation: 'Need more data to determine significance',
      };
    }

    // Simple chi-square test implementation
    const minSamples = 100;
    const totalSent = results.reduce((sum, r) => sum + r.sentCount, 0);

    if (totalSent < minSamples * 2) {
      return {
        isSignificant: false,
        confidence: (totalSent / (minSamples * 2)) * 100,
        recommendation: `Need ${minSamples * 2 - totalSent} more samples for statistical significance`,
      };
    }

    // Calculate variance in open rates
    const avgOpenRate = results.reduce((sum, r) => sum + r.openRate, 0) / results.length;
    const variance = results.reduce((sum, r) => sum + Math.pow(r.openRate - avgOpenRate, 2), 0) / results.length;
    const stdDev = Math.sqrt(variance);

    const isSignificant = stdDev > avgOpenRate * 0.1; // 10% threshold

    return {
      isSignificant,
      confidence: Math.min(100, (totalSent / (minSamples * 2)) * 100),
      recommendation: isSignificant
        ? 'Results are statistically significant. You can declare a winner.'
        : 'Continue running the test for more data.',
    };
  }

  /**
   * Get all tests statistics
   */
  getAllTestsStatistics(): {
    totalTests: number;
    runningTests: number;
    completedTests: number;
    totalVariants: number;
    averageOpenRate: number;
    averageClickRate: number;
  } {
    const tests = Array.from(this.tests.values());
    const runningTests = tests.filter((t) => t.status === 'running').length;
    const completedTests = tests.filter((t) => t.status === 'completed').length;

    let totalVariants = 0;
    let totalOpened = 0;
    let totalSent = 0;
    let totalClicked = 0;

    for (const test of tests) {
      totalVariants += test.variants.length;
      for (const variant of test.variants) {
        totalSent += variant.sentCount;
        totalOpened += variant.openedCount;
        totalClicked += variant.clickedCount;
      }
    }

    return {
      totalTests: tests.length,
      runningTests,
      completedTests,
      totalVariants,
      averageOpenRate: totalSent > 0 ? (totalOpened / totalSent) * 100 : 0,
      averageClickRate: totalSent > 0 ? (totalClicked / totalSent) * 100 : 0,
    };
  }
}

// Export singleton instance
export const abTestingService = new ABTestingService();

export default ABTestingService;
